<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 6.3.0">
  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">


<link rel="stylesheet" href="/lib/font-awesome/css/all.min.css">

<script id="hexo-configurations">
    var NexT = window.NexT || {};
    var CONFIG = {"hostname":"chuangges.gitee.io","root":"/","scheme":"Pisces","version":"7.8.0","exturl":false,"sidebar":{"position":"right","display":"post","padding":18,"offset":12,"onmobile":false},"copycode":{"enable":true,"show_result":true,"style":"mac"},"back2top":{"enable":true,"sidebar":false,"scrollpercent":false},"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"algolia":{"hits":{"per_page":10},"labels":{"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}},"localsearch":{"enable":false,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},"path":"search.xml"};
  </script>

  <meta name="description" content="CentOS 系统、Dumi 文档、GUI 桌面、JS 监控">
<meta property="og:type" content="article">
<meta property="og:title" content="前端应该知道的技能栈">
<meta property="og:url" content="https://chuangges.gitee.io/2022/10/21/17-%E5%89%8D%E7%AB%AF%E6%8A%80%E8%83%BD/index.html">
<meta property="og:site_name" content="闯哥要去流浪了">
<meta property="og:description" content="CentOS 系统、Dumi 文档、GUI 桌面、JS 监控">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://chuangges.gitee.io/images/web/tauri.png">
<meta property="article:published_time" content="2022-10-21T02:49:53.000Z">
<meta property="article:modified_time" content="2023-09-26T06:44:14.359Z">
<meta property="article:author" content="chuangges">
<meta property="article:tag" content="前端技术栈">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://chuangges.gitee.io/images/web/tauri.png">

<link rel="canonical" href="https://chuangges.gitee.io/2022/10/21/17-%E5%89%8D%E7%AB%AF%E6%8A%80%E8%83%BD/">


<script id="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome : false,
    isPost : true,
    lang   : 'zh-CN'
  };
</script>

  <title>前端应该知道的技能栈 | 闯哥要去流浪了</title>
  






  <noscript>
  <style>
  .use-motion .brand,
  .use-motion .menu-item,
  .sidebar-inner,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header { opacity: initial; }

  .use-motion .site-title,
  .use-motion .site-subtitle {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line-before i { left: initial; }
  .use-motion .logo-line-after i { right: initial; }
  </style>
</noscript>

<link rel="alternate" href="/atom.xml" title="闯哥要去流浪了" type="application/atom+xml">
</head>

<body itemscope itemtype="http://schema.org/WebPage">
  <div class="container use-motion">
    <div class="headband"></div>

    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏">
      <span class="toggle-line toggle-line-first"></span>
      <span class="toggle-line toggle-line-middle"></span>
      <span class="toggle-line toggle-line-last"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <span class="logo-line-before"><i></i></span>
      <h1 class="site-title">闯哥要去流浪了</h1>
      <span class="logo-line-after"><i></i></span>
    </a>
      <p class="site-subtitle" itemprop="description">愿你岁月可回首，且以深情共白头</p>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
    </div>
  </div>
</div>




<nav class="site-nav">
  <ul id="menu" class="main-menu menu">
        <li class="menu-item menu-item-home">

    <a href="/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a>

  </li>
        <li class="menu-item menu-item-tags">

    <a href="/tags/" rel="section"><i class="fa fa-tags fa-fw"></i>标签</a>

  </li>
        <li class="menu-item menu-item-categories">

    <a href="/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类</a>

  </li>
        <li class="menu-item menu-item-archives">

    <a href="/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档</a>

  </li>
  </ul>
</nav>




</div>
    </header>

    
  <div class="back-to-top">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>
  <div class="reading-progress-bar"></div>


    <main class="main">
      <div class="main-inner">
        <div class="content-wrap">
          

          <div class="content post posts-expand">
            

    
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://chuangges.gitee.io/2022/10/21/17-%E5%89%8D%E7%AB%AF%E6%8A%80%E8%83%BD/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/chuang.png">
      <meta itemprop="name" content="chuangges">
      <meta itemprop="description" content="chuangge's blog.">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="闯哥要去流浪了">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          前端应该知道的技能栈
        </h1>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2022-10-21 10:49:53" itemprop="dateCreated datePublished" datetime="2022-10-21T10:49:53+08:00">2022-10-21</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2023-09-26 14:44:14" itemprop="dateModified" datetime="2023-09-26T14:44:14+08:00">2023-09-26</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/%E5%89%8D%E7%AB%AF%E6%8A%80%E6%9C%AF%E6%A0%88/" itemprop="url" rel="index"><span itemprop="name">前端技术栈</span></a>
                </span>
            </span>

          <br>
            <span class="post-meta-item" title="本文字数">
              <span class="post-meta-item-icon">
                <i class="far fa-file-word"></i>
              </span>
                <span class="post-meta-item-text">本文字数：</span>
              <span>54k</span>
            </span>
            <span class="post-meta-item" title="阅读时长">
              <span class="post-meta-item-icon">
                <i class="far fa-clock"></i>
              </span>
                <span class="post-meta-item-text">阅读时长 &asymp;</span>
              <span>49 分钟</span>
            </span>
            <div class="post-description">CentOS 系统、Dumi 文档、GUI 桌面、JS 监控</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
        <h1 id="一、CentOS-系统"><a href="#一、CentOS-系统" class="headerlink" title="一、CentOS 系统"></a>一、CentOS 系统</h1><blockquote>
<p>一个基于 Linux 的开源免费操作系统，生产环境部署项目的服务器都是 CentOS 系统。</p>
</blockquote>
<h2 id="Linux-命令"><a href="#Linux-命令" class="headerlink" title="Linux 命令"></a>Linux 命令</h2>  <figure class="highlight py"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 拷贝本地文件 output 到远程服务器 (账号、IP、目录)</span></span><br><span class="line">scp output.txt root@<span class="number">47.93</span><span class="number">.242</span><span class="number">.155</span>:/data/</span><br><span class="line"></span><br><span class="line"><span class="comment"># 拷贝 D 盘 https 目录的所有文件到 远程的 /data</span></span><br><span class="line">scp D:/https/* root@<span class="number">47.93</span><span class="number">.242</span><span class="number">.155</span>:/data</span><br><span class="line"></span><br><span class="line"><span class="comment"># 本地链接远程服务器：端口 用户名@IP</span></span><br><span class="line">ssh -p <span class="number">22</span> root@<span class="number">47.93</span><span class="number">.242</span><span class="number">.155</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># yum 切换为阿里源并查看新缓存 (包含 aliyun 则成功)</span></span><br><span class="line">cd /etc/yum.repos.d/</span><br><span class="line">curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.</span><br><span class="line">aliyun.com/repo/Centos-<span class="number">8.</span>repo</span><br><span class="line">yum makecache</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建目录和文件</span></span><br><span class="line">mkdir /data/test</span><br><span class="line">touch /data/test/index.html</span><br><span class="line"></span><br><span class="line"><span class="comment"># 编辑和查看文件</span></span><br><span class="line">vi /data/test/index.html</span><br><span class="line">cat /data/www/index.html</span><br><span class="line"></span><br><span class="line"><span class="comment"># 复制、删除、移动目录下文件</span></span><br><span class="line">cp –r /data/test/*  /data/newtest</span><br><span class="line">rm -rf /data/newtest/index.html</span><br><span class="line">mv /data/test/*  /data/newtest  </span><br><span class="line"></span><br><span class="line"><span class="comment"># 生成公钥、查看公钥、将公钥拷贝到服务器、将公钥加入信任列表</span></span><br><span class="line">ssh-keygen -t rsa -P <span class="string">&#x27;&#x27;</span></span><br><span class="line">cat .ssh/id_rsa.pub </span><br><span class="line">scp ~/.ssh/id_rsa.pub root@<span class="number">47.98</span><span class="number">.252</span>.XXX:/root</span><br><span class="line">cat id_dsa.pub &gt;&gt; ~/.ssh/authorized_keys</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看当前系统发行版信息、占用端口的进程、杀掉进程</span></span><br><span class="line">cat /etc/redhat-release</span><br><span class="line">netstat -lnp|grep <span class="number">88</span></span><br><span class="line">kill -<span class="number">9</span> <span class="number">1777</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看当前目录、更改当前目录、查看目录文件及子目录</span></span><br><span class="line">pwd</span><br><span class="line">cd [name]</span><br><span class="line">ls、ls -a</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看系统信息：磁盘容量、系统配置、发行版本</span></span><br><span class="line">df -h</span><br><span class="line">cat /proc/cpuinfo </span><br><span class="line">lsb_release -a</span><br><span class="line"></span><br><span class="line"><span class="comment"># 系统进程：当前运行进程、杀死进程、先查找后杀掉进程</span></span><br><span class="line">ps -a、-A、c、e、f   <span class="comment"># 同一终端、所有进程、进程名称、环境变量、程序关系</span></span><br><span class="line">kill -l、-a         <span class="comment"># 编号参数、处理当前进程时不限制命令名和进程号的对应关系</span></span><br><span class="line">kill -p、-s、-u     <span class="comment"># 指定命令只打印进程号而不发送任何信号、指定发送信号、指定用户</span></span><br><span class="line">ps -ef|grep vim    </span><br><span class="line"></span><br><span class="line"><span class="comment"># 网络状况</span></span><br><span class="line">netstat</span><br><span class="line">netstat -a、-at、-au           <span class="comment"># 所有端口、TCP 端口、UDP 端口</span></span><br><span class="line">netstat -l、-lt、-lu、-lx      <span class="comment"># 所有监听端口、监听 TCP、UDP、UNIX</span></span><br><span class="line">netstat -p                    <span class="comment"># PID 和进程名称</span></span><br><span class="line">netstat -r、-rn               <span class="comment"># 核心路由信息、不查询主机名称</span></span><br><span class="line">netstat -antp | grep ssh/<span class="number">22</span>   <span class="comment"># 查看端口和服务</span></span><br><span class="line">tcpdump -nn -i eth0 icmp      <span class="comment"># 网络抓包工具</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># 系统服务管理器：服务状态、服务启动等、所有服务的运行状态或开机自启状态</span></span><br><span class="line">systemctl status service</span><br><span class="line">systemctl start/stop/restart/enable service</span><br><span class="line">systemctl <span class="built_in">list</span>-unit、<span class="built_in">list</span>-ounit-files</span><br><span class="line"></span><br></pre></td></tr></table></figure>


<h2 id="Nginx-服务"><a href="#Nginx-服务" class="headerlink" title="Nginx 服务"></a>Nginx 服务</h2>  <figure class="highlight py"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Centos 下安装 Nginx：一个高性能的 HTTP 和反向代理 web 服务器</span></span><br><span class="line">yum install -y nginx</span><br><span class="line"></span><br><span class="line"><span class="comment"># 启动、重启：http://&lt;您的域名或IP&gt;</span></span><br><span class="line">nginx、nginx -s reload</span><br><span class="line"></span><br><span class="line"><span class="comment"># 打开配置文件配置静态服务器访问路径：提供外网用户访问服务器的 Web 服务</span></span><br><span class="line">vi /etc/nginx/nginx.conf</span><br><span class="line"></span><br><span class="line"><span class="comment"># 配置 /data/www/static 作为所有静态资源请求的根路径</span></span><br><span class="line">server &#123;</span><br><span class="line">    root /data/www;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># /data 目录下创建目录 www：访问路径为 http://&lt;域名或IP&gt;/index.html</span></span><br><span class="line">mkdir -p /data/www</span><br><span class="line">touch /data/www/index.html</span><br><span class="line">vi /data/www/index.html </span><br><span class="line"></span><br><span class="line"><span class="comment"># 配置 SSL 泛域名证书：阿里云等下载证书后将本地证书目录文件上传到远程服务器</span></span><br><span class="line">mkdir /data/cert</span><br><span class="line">scp D:/https/* root@<span class="number">47.93</span><span class="number">.242</span><span class="number">.155</span>:/data/cert</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># 配置示例</span></span><br><span class="line">user nginx;</span><br><span class="line">worker_processes auto;</span><br><span class="line">error_log /var/log/nginx/error.log;</span><br><span class="line">pid /run/nginx.pid;</span><br><span class="line"></span><br><span class="line">include /usr/share/nginx/modules/*.conf;</span><br><span class="line"></span><br><span class="line">events &#123;</span><br><span class="line">  worker_connections <span class="number">1024</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">http &#123;</span><br><span class="line">  log_format main <span class="string">&#x27;$remote_addr - $remote_user [$time_local] &quot;$request&quot; &#x27;</span></span><br><span class="line">                  <span class="string">&#x27;$status $body_bytes_sent &quot;$http_referer&quot; &#x27;</span></span><br><span class="line">                  <span class="string">&#x27;&quot;$http_user_agent&quot; &quot;$http_x_forwarded_for&quot;&#x27;</span>;</span><br><span class="line"></span><br><span class="line">  access_log  /var/log/nginx/access.log  main;</span><br><span class="line"></span><br><span class="line">  sendfile            on;</span><br><span class="line">  tcp_nopush          on;</span><br><span class="line">  tcp_nodelay         on;</span><br><span class="line">  keepalive_timeout   <span class="number">65</span>;</span><br><span class="line">  types_hash_max_size <span class="number">2048</span>;</span><br><span class="line"></span><br><span class="line">  include             /etc/nginx/mime.types;</span><br><span class="line">  default_type        application/octet-stream;</span><br><span class="line"></span><br><span class="line">  include /etc/nginx/conf.d/*.conf;</span><br><span class="line"></span><br><span class="line">  server &#123;</span><br><span class="line">      <span class="comment"># 默认只监听 80 端口</span></span><br><span class="line">      listen       <span class="number">80</span>;</span><br><span class="line">      <span class="comment"># 修改域名</span></span><br><span class="line">      server_name  nsuedu.cn;      </span><br><span class="line">      <span class="comment"># HTTP 请求则重定向到 HTTPS</span></span><br><span class="line">      rewrite ^(.*) https://$host$<span class="number">1</span> permanent;  </span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  server &#123;</span><br><span class="line">      <span class="comment"># 开启 443 监听，开启 ssl</span></span><br><span class="line">      listen       <span class="number">443</span> ssl http2 default_server;</span><br><span class="line">      listen       [::]:<span class="number">443</span> ssl http2 default_server;</span><br><span class="line"></span><br><span class="line">      <span class="comment"># 域名</span></span><br><span class="line">      server_name  nsuedu.cn;</span><br><span class="line"></span><br><span class="line">      <span class="comment"># 静态文件的路径</span></span><br><span class="line">      root         /data/www;</span><br><span class="line"></span><br><span class="line">      <span class="comment"># ssl 证书配置</span></span><br><span class="line">      <span class="comment"># 修改证书路径一</span></span><br><span class="line">      ssl_certificate <span class="string">&quot;/data/cert/4726867_www.nsuedu.cn.pem&quot;</span>;</span><br><span class="line">      <span class="comment"># 修改证书路径二</span></span><br><span class="line">      ssl_certificate_key <span class="string">&quot;/data/cert/4726867_www.nsuedu.cn.key&quot;</span>;</span><br><span class="line"></span><br><span class="line">      ssl_session_cache shared:SSL:1m;</span><br><span class="line">      ssl_session_timeout  10m;         <span class="comment"># 缓存有效期</span></span><br><span class="line">      ssl_ciphers HIGH:!aNULL:!MD5;     <span class="comment"># 加密算法</span></span><br><span class="line">      ssl_prefer_server_ciphers on;     <span class="comment"># 使用服务器端的首选算法</span></span><br><span class="line"></span><br><span class="line">      include /etc/nginx/default.d/*.conf;</span><br><span class="line"></span><br><span class="line">      location / &#123;</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      <span class="comment"># 配置反向代理</span></span><br><span class="line">      location /api/ &#123;</span><br><span class="line">          proxy_pass http://<span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">18081</span>;</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      error_page <span class="number">404</span> /<span class="number">404.</span>html;</span><br><span class="line">          location = /40x.html &#123;</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      error_page <span class="number">500</span> <span class="number">502</span> <span class="number">503</span> <span class="number">504</span> /50x.html;</span><br><span class="line">          location = /50x.html &#123;</span><br><span class="line">      &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="NodeJS-环境"><a href="#NodeJS-环境" class="headerlink" title="NodeJS 环境"></a>NodeJS 环境</h2>  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 通过 EPEL 库安装，但安装版本较低并且需要手动切换版本</span></span><br><span class="line">sudo dnf <span class="variable language_">module</span> list nodejs  <span class="comment">// 查看系统自带版本</span></span><br><span class="line">sudo dnf <span class="variable language_">module</span> enable <span class="attr">nodejs</span>:<span class="number">14</span></span><br><span class="line">sudo dnf install nodejs</span><br><span class="line">node --v</span><br><span class="line"></span><br><span class="line"><span class="comment">// 通过 NVM 安装方便快捷，但安装时国外的网不好</span></span><br><span class="line">curl -o- <span class="attr">https</span>:<span class="comment">//raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash</span></span><br><span class="line">source ~/.<span class="property">bash_profile</span></span><br><span class="line">nvm install --lts  </span><br><span class="line">echo $PATH         <span class="comment">// 查看安装位置</span></span><br></pre></td></tr></table></figure>

<h2 id="Docker-部署"><a href="#Docker-部署" class="headerlink" title="Docker 部署"></a>Docker 部署</h2><blockquote>
<p>Docker 是一个开源的应用容器引擎，用于创建包含项目镜像的可移植容器并发布到 Linux 机器而实现虚拟化，核心概念有 <code>Image 镜像</code> (包含应用运行代码及其依赖和配置文件的可执行包)、<code>Container 容器</code> (镜像的运行时实例，相互之间没有任何接口的沙箱机制)、<code>Repository 仓库</code> (存储可复制的镜像)。Docker 被请求运行容器时会在计算机上创建独立进程的隔离环境和应用及其依赖的镜像，然后通过执行预定命令来运行应用。以下为常见的部署方式：</p>
</blockquote>
<ul>
<li><strong>物理服务器直接部署</strong>：缺点是部署缓慢 (安装操作系统、应用程序、依赖软件等)、部署和迁移成本高 (简单应用也要一台服务器而且迁移时需要全部重新部署)、不能充分利用资源。</li>
<li><strong>通过虚拟机部署</strong>：虚拟机指在物理计算机通过软件层分出部分硬件资源而模拟具有完整功能但运行在独立环境的系统，一台服务器上模拟多台虚拟机就可以安装多个相互隔离的应用程序。优点有：可达到硬件资源的最大化利用、更容易扩展应用、虚拟出不同的物理资源而可以快速搭建云服务。缺点则是每台虚拟机都需要模拟整个操作系统而造成占用空间大和性能较差。</li>
<li><strong>通过轻量级容器技术 Docker 部署</strong>：容器指将操作系统层虚拟化的一个软件单元，通过底层 Linux 技术对进程进行封装为隔离于宿主机的独立进程。相比虚拟机在物理资源层面的隔离，Docker 在操作系统进程层面的隔离实现了小环境的轻量级虚拟。</li>
</ul>
  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br></pre></td><td class="code"><pre><span class="line">==================== <span class="title class_">Docker</span> 入门 =======================</span><br><span class="line"><span class="comment">// 1、安装和配置镜像加速，建议去官网下载方便操作的 Docker 桌面端</span></span><br><span class="line">brew cask install docker</span><br><span class="line">docker -v </span><br><span class="line">&#123; </span><br><span class="line">  <span class="string">&quot;registry-mirrors&quot;</span>: [</span><br><span class="line">      <span class="string">&quot;http://hub-mirror.c.163.com/&quot;</span>,</span><br><span class="line">      <span class="string">&quot;https://registry.docker-cn.com&quot;</span></span><br><span class="line">  ],</span><br><span class="line">  <span class="string">&quot;insecure-registries&quot;</span>:[],</span><br><span class="line">  <span class="string">&quot;experimental&quot;</span>: <span class="literal">false</span>,</span><br><span class="line">  <span class="string">&quot;debug&quot;</span>: <span class="literal">true</span></span><br><span class="line">&#125; </span><br><span class="line"></span><br><span class="line"><span class="comment">// 2、项目实践：前端项目一般分为 Nginx 静态部署、Node 服务</span></span><br><span class="line">vue create docker-demo </span><br><span class="line">npm run build</span><br><span class="line">cd docker-demo</span><br><span class="line"></span><br><span class="line"><span class="comment">// 新建并配置镜像 Dockerfile</span></span><br><span class="line">touch <span class="title class_">Dockerfile</span>       </span><br><span class="line"><span class="variable constant_">FROM</span> nginx             </span><br><span class="line"><span class="variable constant_">COPY</span> dist/ <span class="regexp">/usr/</span>share/nginx/html/  </span><br><span class="line"><span class="variable constant_">COPY</span> <span class="keyword">default</span>.<span class="property">conf</span> /etc/nginx/conf.<span class="property">d</span>/<span class="keyword">default</span>.<span class="property">conf</span>  </span><br><span class="line"></span><br><span class="line"><span class="comment">// 桌面端拉取 Nginx 镜像并配置 Nginx</span></span><br><span class="line">docker pull nginx      </span><br><span class="line">touch <span class="keyword">default</span>.<span class="property">conf</span></span><br><span class="line">server &#123;</span><br><span class="line">    listen       <span class="number">80</span>;</span><br><span class="line">    server_name  localhost;</span><br><span class="line"></span><br><span class="line">    #charset koi8-r;</span><br><span class="line">    access_log  /<span class="keyword">var</span>/log/nginx/host.<span class="property">access</span>.<span class="property">log</span>  main;</span><br><span class="line">    error_log  /<span class="keyword">var</span>/log/nginx/error.<span class="property">log</span>  error;</span><br><span class="line"></span><br><span class="line">    location / &#123;</span><br><span class="line">        root   /usr/share/nginx/html;</span><br><span class="line">        index  index.<span class="property">html</span> index.<span class="property">htm</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    error_page   <span class="number">500</span> <span class="number">502</span> <span class="number">503</span> <span class="number">504</span>  /50x.<span class="property">html</span>;</span><br><span class="line">    location = /50x.<span class="property">html</span> &#123;</span><br><span class="line">        root   /usr/share/nginx/html;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 构建镜像：-t 给镜像命名、. 基于当前目录 Dockerfile</span></span><br><span class="line">docker build -t jartto-docker-demo .  </span><br><span class="line">docker image ls | grep jartto-docker-demo   </span><br><span class="line"></span><br><span class="line"><span class="comment">// 运行容器和查看：-d 后台运行、-p 端口映射、--name 设置容器名</span></span><br><span class="line">docker run -d -p <span class="number">3000</span>:<span class="number">80</span> --name docker-vue jartto-docker-demo</span><br><span class="line">docker ps -a </span><br><span class="line"></span><br><span class="line"><span class="comment">// 访问项目</span></span><br><span class="line">curl -v -i <span class="attr">localhost</span>:<span class="number">3000</span>  </span><br><span class="line"></span><br><span class="line"><span class="comment">// 发布镜像：注册、登录、推送</span></span><br><span class="line"><span class="attr">https</span>:<span class="comment">//hub.docker.com </span></span><br><span class="line">docker login   </span><br><span class="line">docker tag &lt;image&gt; &lt;username&gt;/&lt;repository&gt;:&lt;tag&gt;         </span><br></pre></td></tr></table></figure>

  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br></pre></td><td class="code"><pre><span class="line">==================== <span class="title class_">CentOS</span> 系统集成 <span class="title class_">Docker</span> =======================</span><br><span class="line"><span class="comment">// 安装依赖：Linux 的一个存储驱动、Lvm 则用于创建逻辑磁盘分区</span></span><br><span class="line">yum install -y yum-utils device-mapper-persistent-data lvm2</span><br><span class="line"></span><br><span class="line"><span class="comment">// 添加阿里云的 Docker 镜像源加速安装</span></span><br><span class="line">sudo yum-config-manager --add-repo <span class="attr">http</span>:<span class="comment">//mirrors.aliyun.com/docker-ce</span></span><br><span class="line"><span class="regexp">/linux/</span>centos/docker-ce.<span class="property">repo</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 安装、通过 Linux 的进程管理服务命令 systemctl 启动、设置开机启动</span></span><br><span class="line">yum install docker-ce -y</span><br><span class="line">systemctl start/enable docker</span><br><span class="line">chkconfig docker on</span><br><span class="line"></span><br><span class="line"><span class="comment">// 本地镜像的显示</span></span><br><span class="line">docker images</span><br><span class="line">docker rmi imageID</span><br><span class="line"></span><br><span class="line"><span class="comment">// 查看容器版本/列表、容器的删除/启动/关闭/重启</span></span><br><span class="line">docker -v/ps</span><br><span class="line">docker rm/start/stop/restart/ containerID</span><br><span class="line"></span><br><span class="line"><span class="comment">// 配置阿里云镜像源：官方源拉取镜像外网太慢</span></span><br><span class="line">登录阿里云官网、阿里云容器镜像服务、左侧菜单的镜像加速器、选择 <span class="title class_">CentOS</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@title</span> Docker 内配置 Nginx 方法一</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> -d：在后台运行</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> -p：端口映射，指定容器内部端口 80 映射到宿主机端口 4030</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> --rm：容器停止运行后，自动删除容器文件</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> --name：容器名字为 mynginx</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> nginx：根据 nginx 镜像运行容器</span></span><br><span class="line"><span class="comment">**/</span></span><br><span class="line">docker container run -d -p <span class="number">4030</span>:<span class="number">80</span> --rm --name mynginx nginx</span><br><span class="line">docker container stop mynginx     <span class="comment">// 容器终止：容器文件会自动删除</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@title</span> Docker 内配置 Nginx 方法二</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> --name：设置容器名称</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> -d：在后台运行</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> -p：端口映射</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> nginx：根据 nginx 镜像运行容器</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * <span class="doctag">@desp</span> 浏览器访问 http://47.93.242.155:4030/</span></span><br><span class="line"><span class="comment">**/</span></span><br><span class="line">docker pull nginx   <span class="comment">// 下载一个官方 Nginx 镜像到本地</span></span><br><span class="line">docker images       <span class="comment">// 查看本地镜像列表</span></span><br><span class="line">docker run --name=nginx -d -p <span class="number">4030</span>:<span class="number">80</span> nginx </span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@title</span> Docker 内配置 Nginx 反向代理</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> -it：表示分配一个伪终端</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> nginx：容器名称，也可用容器 ID</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> /bin/bash：对容器执行 bash 操作</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * <span class="doctag">@desp</span> 首先通过以下命令进入 Nginx 容器</span></span><br><span class="line"><span class="comment">**/</span></span><br><span class="line">docker exec -it nginx /bin/bash</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 查看操作系统版本(发现是一个Debian系统)、查看当前系统版本</span></span><br><span class="line">cat /etc/issue</span><br><span class="line">cat /etc/os-release</span><br><span class="line"></span><br><span class="line"><span class="comment">// Debian 系统的包管理工具：更新、安装vim、替换 apt 软件源</span></span><br><span class="line">apt-get update</span><br><span class="line">apt-get install vim</span><br><span class="line">sudo apt install apt-transport-https ca-certificates</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/** </span></span><br><span class="line"><span class="comment"> * <span class="doctag">@title</span> debian 源的配置文件分为两部分</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> /etc/apt/source.list：主配置文件</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> /etc/apt/sources.list.d/*.list：辅助文件</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * <span class="doctag">@title</span> 修改为清华源镜像</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> deb：对应预编译的二进制包，deb-src 可替换后新增源码配置行</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> <span class="variable">deb</span>-src：对应软件的源码包(一般不需要)，注释可提高 apt update 速度</span></span><br><span class="line"><span class="comment">**/</span> </span><br><span class="line">vi /etc/apt/sources.<span class="property">list</span></span><br><span class="line"></span><br><span class="line"># 末尾都添加 main contrib non-free</span><br><span class="line">deb <span class="attr">https</span>:<span class="comment">//mirrors.tuna.tsinghua.edu.cn/debian/ buster</span></span><br><span class="line"># deb-src <span class="attr">https</span>:<span class="comment">//mirrors.tuna.tsinghua.edu.cn/debian/ buster</span></span><br><span class="line">deb <span class="attr">https</span>:<span class="comment">//mirrors.tuna.tsinghua.edu.cn/debian/ buster-updates</span></span><br><span class="line">deb <span class="attr">https</span>:<span class="comment">//mirrors.tuna.tsinghua.edu.cn/debian/ buster-backports</span></span><br><span class="line">deb <span class="attr">https</span>:<span class="comment">//mirrors.tuna.tsinghua.edu.cn/debian-security buster/updates</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// Nginx 的查看、配置(同上 nginx)、新建目录、将静态文件、SSL 证书等资源文件传给容器</span></span><br><span class="line">cat /etc/nginx/nginx.<span class="property">conf</span></span><br><span class="line">vi /etc/nginx/conf.<span class="property">d</span>/<span class="keyword">default</span>.<span class="property">conf</span></span><br><span class="line">mkdir /data/cert、mkdir /data/www</span><br><span class="line">docker cp /data/cert 6<span class="attr">ba3901beac9</span>:<span class="regexp">/data/</span>cert </span><br><span class="line">docker commit -m=<span class="string">&quot;备注&quot;</span> 容器<span class="variable constant_">ID</span> 镜像<span class="variable constant_">ID</span></span><br></pre></td></tr></table></figure>

<h2 id="Jenkins-工具"><a href="#Jenkins-工具" class="headerlink" title="Jenkins 工具"></a>Jenkins 工具</h2><blockquote>
<p>一个基于 Java 开发的持续构建工具平台，主要用于持续、自动的构建&#x2F;测试软件和项目。它可以执行预定的设置和构建脚本，也可以集成 Git 代码库，实现自动触发和定时触发构建。</p>
</blockquote>
  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// yum 包管理器安装 java</span></span><br><span class="line">yum install -y java</span><br><span class="line"></span><br><span class="line"><span class="comment">// 清华大学镜像站下载到目录 /data </span></span><br><span class="line">cd  /data</span><br><span class="line">wget <span class="attr">https</span>:<span class="comment">//mirrors.tuna.tsinghua.edu.cn/jenkins</span></span><br><span class="line"><span class="regexp">/redhat-stable/</span>jenkins-<span class="number">2.332</span><span class="number">.3</span>-<span class="number">1.1</span>.<span class="property">noarch</span>.<span class="property">rpm</span></span><br><span class="line">sudo yum install jenkins-<span class="number">2.332</span><span class="number">.3</span>-<span class="number">1.1</span>.<span class="property">noarch</span>.<span class="property">rpm</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 启动、重启、停止</span></span><br><span class="line">service jenkins start/restart/stop</span><br><span class="line"></span><br><span class="line"><span class="comment">// 初始化配置：查看密码并输入后解锁、执行替换插件源命令后下载插件、注册账号</span></span><br><span class="line">cat /<span class="keyword">var</span>/lib/jenkins/secrets/initialAdminPassword</span><br><span class="line"></span><br><span class="line">sed -i <span class="string">&#x27;s/http:\/\/updates.jenkins-ci.org\/download/https:\/\/</span></span><br><span class="line"><span class="string">mirrors.tuna.tsinghua.edu.cn\/jenkins/g&#x27;</span> <span class="keyword">default</span>.<span class="property">json</span> &amp;&amp; sed -i </span><br><span class="line"><span class="string">&#x27;s/http:\/\/www.google.com/https:\/\/www.baidu.com/g&#x27;</span> <span class="keyword">default</span>.<span class="property">json</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 新增 docker 用户组，解决命令权限问题</span></span><br><span class="line">sudo groupadd docker          </span><br><span class="line">sudo gpasswd -a jenkins docker</span><br><span class="line">newgrp docker</span><br><span class="line">sudo service jenkins restart</span><br><span class="line"></span><br><span class="line"><span class="comment">// 测试：Jenkins 首页、左侧导航、新建任务、Freestyle project、构建后增加步骤执行</span></span><br><span class="line">docker pull <span class="attr">node</span>:latest       <span class="comment">// 保存后点击左侧菜单的立即构建，然后查看历史记录</span></span><br><span class="line">docker images                 <span class="comment">// docker 镜像下可看到新增了 node</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 配置 node</span></span><br><span class="line">docker run -it node /bin/bash</span><br><span class="line"></span><br><span class="line">npm config set registry <span class="attr">https</span>:<span class="comment">//registry.npm.taobao.org</span></span><br><span class="line">npm get registry</span><br><span class="line">npm install yarn -g   <span class="comment">// 如果提示已安装就不用再安装了</span></span><br><span class="line">yarn config set registry <span class="attr">http</span>:<span class="comment">//registry.npm.taobao.org/</span></span><br><span class="line">yarn config get registry</span><br><span class="line"></span><br><span class="line">         </span><br><span class="line"><span class="comment">// 使用 SSH 协议集成 Git 仓库源：Gitee 作为代码源</span></span><br><span class="line">yum -y install git</span><br><span class="line">ssh-keygen -t rsa -C <span class="string">&quot;642178633@qq.com&quot;</span>  <span class="comment">// 生成公钥私钥</span></span><br><span class="line">cat ~<span class="regexp">/.ssh/i</span>d_rsa.<span class="property">pub</span>                    <span class="comment">// Gitee 配置公钥</span></span><br><span class="line">cat ~<span class="regexp">/.ssh/i</span>d_rsa                        <span class="comment">// Jenkins 配置私钥</span></span><br><span class="line">项目的配置、源码管理、<span class="title class_">Git</span>、<span class="title class_">Repositories</span>、<span class="title class_">Credentials</span> 右侧添加、</span><br><span class="line">选择类型中的 <span class="variable constant_">SSH</span> <span class="title class_">Username</span> <span class="keyword">with</span> private key、填写信息后 <span class="title class_">Add</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// Dockerfile 文件编写：镜像生成步骤的描述文件</span></span><br><span class="line">vi <span class="title class_">Dockerfile</span></span><br><span class="line"><span class="variable constant_">FROM</span> <span class="attr">nginx</span>:<span class="number">1.15</span>-alpine</span><br><span class="line"><span class="variable constant_">COPY</span> html /etc/nginx/html</span><br><span class="line"><span class="variable constant_">COPY</span> conf /etc/nginx/</span><br><span class="line"><span class="variable constant_">WORKDIR</span> /etc/nginx/html</span><br><span class="line"><span class="comment">// 构建镜像：-t 声明 镜像名:版本 . 声明当前路径寻找 Dockerfile 文件</span></span><br><span class="line">docker build -t <span class="attr">imagename</span>:version .</span><br><span class="line"><span class="comment">// 提交镜像</span></span><br><span class="line">git add ./<span class="title class_">Dockerfile</span></span><br><span class="line">git commit -m <span class="string">&quot;chore: add dockerfile&quot;</span></span><br><span class="line">git push</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// Jenkins 端配置：新建脚本、项目的配置 -- 构建 -- Execute shell</span></span><br><span class="line">touch /data/jenkins-shll.<span class="property">sh</span></span><br><span class="line">vi /data/jenkins-shll.<span class="property">sh</span></span><br><span class="line">#!<span class="regexp">/bin/</span>sh -l</span><br><span class="line">yarn             <span class="comment">// 安装依赖</span></span><br><span class="line">yarn run build   <span class="comment">// 构建文件</span></span><br><span class="line">docker build -t jenkins-test .   <span class="comment">// 构建镜像</span></span><br><span class="line"></span><br><span class="line">sh /data/jenkins-shll.<span class="property">sh</span>   <span class="comment">// Execute shell  </span></span><br><span class="line"></span><br><span class="line">docker images  <span class="comment">// 又多了一个 jenkins-test 名称的 docker 镜像</span></span><br></pre></td></tr></table></figure>


<h1 id="二、Dumi-组件文档"><a href="#二、Dumi-组件文档" class="headerlink" title="二、Dumi 组件文档"></a>二、Dumi 组件文档</h1><h2 id="构建文档项目"><a href="#构建文档项目" class="headerlink" title="构建文档项目"></a>构建文档项目</h2><blockquote>
<p>静态站点框架主要有：Dumi、Storybook、Gitbook、VuePress、VitePress、Docsify。</p>
</blockquote>
  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@title</span> 1、初始化目录</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@desp</span>  npx：npm5.2 新增的执行包，依赖包不存在时自动下载</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@desp</span>  dumi 开发组件，father-build 将源码 md/ts 解析为 React</span></span><br><span class="line"><span class="comment">**/</span></span><br><span class="line">npx @umijs/create-dumi-lib         <span class="comment">// 文档模式：只包含组件文档</span></span><br><span class="line">npx @umijs/create-dumi-lib --site  <span class="comment">// 站点模式：完整的组件库网站</span></span><br><span class="line">npx @umijs/create-dumi-app         <span class="comment">// 纯静态站点：不包含组件源码</span></span><br><span class="line"></span><br><span class="line">npx @umijs/create-dumi-lib --site</span><br><span class="line">npm install、npm start、npm run build</span><br><span class="line"></span><br><span class="line">├── <span class="variable constant_">README</span>.<span class="property">md</span></span><br><span class="line">├── docs                 <span class="comment">// 文档目录</span></span><br><span class="line">│   ├── index.<span class="property">md</span>         <span class="comment">// 文档首页</span></span><br><span class="line">│   └── otherDir         <span class="comment">// 文档其它路由</span></span><br><span class="line">│   		├── index.<span class="property">md</span></span><br><span class="line">│   		├── sample.<span class="property">md</span></span><br><span class="line">│   		└── help.<span class="property">md</span></span><br><span class="line">├── src                  <span class="comment">// 源码目录</span></span><br><span class="line">│   ├── index.<span class="property">ts</span>         <span class="comment">// 入口文件</span></span><br><span class="line">│   └── <span class="title class_">Foo</span>              <span class="comment">// 组件源码</span></span><br><span class="line">│       ├── index.<span class="property">md</span> </span><br><span class="line">│   		└── index.<span class="property">ts</span></span><br><span class="line">├── .<span class="property">umirc</span>.<span class="property">ts</span>            <span class="comment">// dumi 配置文件</span></span><br><span class="line">└── .<span class="property">fatherrc</span>.<span class="property">ts</span>         <span class="comment">// father-build 打包配置文件</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 2、新建组件 /src/MyButton/index.md：npm install antd -D</span></span><br><span class="line">---</span><br><span class="line"><span class="attr">title</span>: 页面名称</span><br><span class="line"><span class="attr">nav</span>:</span><br><span class="line">  <span class="attr">title</span>: <span class="title class_">Components</span>   <span class="comment">// 导航名称</span></span><br><span class="line">  <span class="attr">path</span>: <span class="regexp">/components   /</span><span class="regexp">/ 导航路由</span></span><br><span class="line"><span class="regexp">  order: 0            /</span><span class="regexp">/ 导航顺序，数字越小越靠前</span></span><br><span class="line"><span class="regexp">group:</span></span><br><span class="line"><span class="regexp">  title: MyButton</span></span><br><span class="line"><span class="regexp">  path: /</span>btn          <span class="comment">// 分组路由 = 导航路由+自己</span></span><br><span class="line">  <span class="attr">order</span>: <span class="number">1</span>            <span class="comment">// 分组顺序，默认以路径长度和字典序排序</span></span><br><span class="line">---</span><br><span class="line"></span><br><span class="line">## <span class="title class_">MyButton</span></span><br><span class="line"><span class="comment">// ```tsx    使用时不注释</span></span><br><span class="line"><span class="keyword">import</span> <span class="title class_">React</span> <span class="keyword">from</span> <span class="string">&#x27;react&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">MyButton</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;my-dumi-doc&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> () =&gt; <span class="language-xml"><span class="tag">&lt;<span class="name">MyButton</span> <span class="attr">type</span>=<span class="string">&quot;primary&quot;</span>&gt;</span>click me!<span class="tag">&lt;/<span class="name">MyButton</span>&gt;</span></span>;</span><br><span class="line"><span class="comment">// ```</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// /src/index.ts：暴露组件</span></span><br><span class="line"><span class="keyword">export</span> &#123; <span class="keyword">default</span> <span class="keyword">as</span> <span class="title class_">MyButton</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;./MyButton&#x27;</span>;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// MyButton/index.tsx</span></span><br><span class="line"><span class="keyword">import</span> <span class="title class_">React</span> <span class="keyword">from</span> <span class="string">&#x27;react&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">Button</span>, <span class="title class_">ButtonProps</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;antd&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="string">&#x27;antd/dist/antd.css&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> interface <span class="title class_">MyButtonProps</span> <span class="keyword">extends</span> <span class="title class_">ButtonProps</span> &#123;</span><br><span class="line">    isMy?: boolean;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="title class_">MyButton</span>: <span class="title class_">React</span>.<span class="property">FC</span>&lt;<span class="title class_">MyButtonProps</span>&gt; = <span class="function">(<span class="params">props</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> &#123; children, ...restProps &#125; = props;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> (</span><br><span class="line">        <span class="language-xml"><span class="tag">&lt;<span class="name">Button</span> &#123;<span class="attr">...restProps</span>&#125;&gt;</span>&#123;children&#125;<span class="tag">&lt;/<span class="name">Button</span>&gt;</span></span></span><br><span class="line">    );</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="title class_">MyButton</span>;</span><br></pre></td></tr></table></figure>


<h2 id="lerna-管理包"><a href="#lerna-管理包" class="headerlink" title="lerna 管理包"></a>lerna 管理包</h2><blockquote>
<p>用来优化托管在 git&#x2F;npm 上的多包代码库的工作流的一个管理工具，可以让你在主项目下管理多个子项目，从而解决了多个包互相依赖并且发布时需要手动维护多个包的问题。</p>
</blockquote>
  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 根目录初始化：自动生成 lerna.json、packages</span></span><br><span class="line">npm install -g lerna、lerna init</span><br><span class="line">lerna create @keith/button packages/button --yes</span><br><span class="line"></span><br><span class="line">lerna init       <span class="comment">// 初始化配置</span></span><br><span class="line">lerna create     <span class="comment">// 创建包</span></span><br><span class="line">lerna link       <span class="comment">// 链接包</span></span><br><span class="line">lerna add        <span class="comment">// 安装单个依赖</span></span><br><span class="line">lerna bootstrap  <span class="comment">// 安装全局依赖并链接到所有子包</span></span><br><span class="line">lerna clean      <span class="comment">// 清除 node_modules</span></span><br><span class="line">lerna list       <span class="comment">// 列出子包</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// tsconfig.json</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">&quot;compilerOptions&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;declaration&quot;</span>: <span class="literal">true</span>,</span><br><span class="line">    <span class="string">&quot;paths&quot;</span>: &#123;  <span class="comment">// 此处新增组件目录</span></span><br><span class="line">      <span class="string">&quot;@keith/button&quot;</span>: [<span class="string">&quot;./packages/button/src/index.tsx&quot;</span>]</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// .fatherrc.ts：添加 button</span></span><br><span class="line"><span class="keyword">const</span> <span class="attr">headPkgs</span>: string[] = [<span class="string">&#x27;button&#x27;</span>]; </span><br><span class="line"></span><br><span class="line"><span class="comment">// package.json：添加 workspaces</span></span><br><span class="line"> <span class="string">&quot;workspaces&quot;</span>: [</span><br><span class="line">  <span class="string">&quot;packages/*&quot;</span></span><br><span class="line">],</span><br><span class="line"><span class="string">&quot;scripts&quot;</span>: &#123;</span><br><span class="line">  <span class="string">&quot;start&quot;</span>: <span class="string">&quot;dumi dev&quot;</span>,</span><br><span class="line">  <span class="string">&quot;docs:build&quot;</span>: <span class="string">&quot;dumi build&quot;</span>,</span><br><span class="line">  <span class="string">&quot;docs:deploy&quot;</span>: <span class="string">&quot;gh-pages -d docs-dist&quot;</span>,</span><br><span class="line">  <span class="string">&quot;build&quot;</span>: <span class="string">&quot;father-build&quot;</span>,</span><br><span class="line">  <span class="string">&quot;deploy&quot;</span>: <span class="string">&quot;npm run docs:build &amp;&amp; npm run docs:deploy&quot;</span>,</span><br><span class="line">  <span class="string">&quot;prettier&quot;</span>: <span class="string">&quot;prettier --write \&quot;**/*.&#123;js,jsx,tsx,ts,less,md,json&#125;\&quot;&quot;</span>,</span><br><span class="line">  <span class="string">&quot;test&quot;</span>: <span class="string">&quot;umi-test&quot;</span>,</span><br><span class="line">  <span class="string">&quot;test:coverage&quot;</span>: <span class="string">&quot;umi-test --coverage&quot;</span>,</span><br><span class="line">  <span class="string">&quot;prepublishOnly&quot;</span>: <span class="string">&quot;npm run build&quot;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 去除多余目录：组件目录 lib/_test_、根目录 src</span></span><br></pre></td></tr></table></figure>

<h2 id="Prettier-代码风格"><a href="#Prettier-代码风格" class="headerlink" title="Prettier 代码风格"></a>Prettier 代码风格</h2>  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">npm install @umijs/fabric -D</span><br><span class="line"></span><br><span class="line"><span class="comment">// .prettierrc.js</span></span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = <span class="built_in">require</span>(<span class="string">&#x27;@umijs/fabric&#x27;</span>).<span class="property">prettier</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// .eslintrc.js</span></span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = &#123;</span><br><span class="line">  <span class="attr">extends</span>: [<span class="built_in">require</span>.<span class="title function_">resolve</span>(<span class="string">&#x27;@umijs/fabric/dist/eslint&#x27;</span>)],</span><br><span class="line">&#125; </span><br></pre></td></tr></table></figure>


<h2 id="GitHooks-提交规范"><a href="#GitHooks-提交规范" class="headerlink" title="GitHooks 提交规范"></a>GitHooks 提交规范</h2><blockquote>
<p><code>GitHooks</code> 是指在 git 操作时触发运行 .git&#x2F;hooks 目录下的钩子脚本，自定义钩子操作时如果直接修改 hooks 脚本并去掉 sample 后缀即可生效但是不能提交修改代码，所以一般通过引入工具 husky&#x2F;yorkie 来配置。<code>Commitlin</code> 用来配置提交信息校验规则的工具。</p>
</blockquote>
  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line">npm install @umijs/yorkie -D</span><br><span class="line">npm install @commitlint/cli @commitlint/config-conventional -D</span><br><span class="line"></span><br><span class="line"><span class="comment">// package.json</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">&quot;gitHooks&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;pre-commit&quot;</span>: <span class="string">&quot;lint-staged&quot;</span>,</span><br><span class="line">    <span class="string">&quot;commit-msg&quot;</span>: <span class="string">&quot;commitlint -E GIT_PARAMS&quot;</span></span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="string">&quot;lint-staged&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;packages/**/*.&#123;js, ts, jsx, tsx, md, json&#125;&quot;</span>: [</span><br><span class="line">      <span class="string">&quot;eslint --fix&quot;</span>,</span><br><span class="line">      <span class="string">&quot;prettier --write&quot;</span>,</span><br><span class="line">      <span class="string">&quot;git add .&quot;</span></span><br><span class="line">    ]</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// .commitlintrc.js / commitlint.config.js </span></span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = &#123;</span><br><span class="line">  <span class="attr">extends</span>: [<span class="string">&#x27;@commitlint/config-conventional&#x27;</span>],</span><br><span class="line">  <span class="attr">rules</span>: &#123;</span><br><span class="line">    <span class="string">&#x27;type-enum&#x27;</span>: [</span><br><span class="line">      <span class="number">2</span>,</span><br><span class="line">      <span class="string">&#x27;always&#x27;</span>,</span><br><span class="line">      [</span><br><span class="line">        <span class="string">&#x27;feature&#x27;</span>,    <span class="comment">// 开发新功能</span></span><br><span class="line">        <span class="string">&#x27;fix&#x27;</span>,        <span class="comment">// 修补 bug</span></span><br><span class="line">        <span class="string">&#x27;refactor&#x27;</span>,   <span class="comment">// 代码重构</span></span><br><span class="line">        <span class="string">&#x27;docs&#x27;</span>,       <span class="comment">// 文档</span></span><br><span class="line">        <span class="string">&#x27;style&#x27;</span>,      <span class="comment">// 格式（不影响代码运行的变动）</span></span><br><span class="line">        <span class="string">&#x27;test&#x27;</span>,       <span class="comment">// 增加测试</span></span><br><span class="line">        <span class="string">&#x27;build&#x27;</span>,      <span class="comment">// 变更项目构建或外部依赖（webpack、package等）</span></span><br><span class="line">        <span class="string">&#x27;revert&#x27;</span>,     <span class="comment">// 回滚</span></span><br><span class="line">        <span class="string">&#x27;perf&#x27;</span>,       <span class="comment">// 改善性能</span></span><br><span class="line">      ],</span><br><span class="line">    ],</span><br><span class="line">    <span class="string">&#x27;type-empty&#x27;</span>: [<span class="number">2</span>, <span class="string">&#x27;never&#x27;</span>],       <span class="comment">// 不符合规范时，可提交但有警告</span></span><br><span class="line">    <span class="string">&#x27;subject-empty&#x27;</span>: [<span class="number">2</span>, <span class="string">&#x27;never&#x27;</span>],    <span class="comment">// 不符合规范时，可提交但有警告</span></span><br><span class="line">    <span class="string">&#x27;subject-full-stop&#x27;</span>: [<span class="number">0</span>, <span class="string">&#x27;never&#x27;</span>],</span><br><span class="line">    <span class="string">&#x27;subject-case&#x27;</span>: [<span class="number">0</span>, <span class="string">&#x27;never&#x27;</span>]</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>


<h1 id="三、GUI-桌面应用"><a href="#三、GUI-桌面应用" class="headerlink" title="三、GUI 桌面应用"></a>三、GUI 桌面应用</h1><blockquote>
<p>基于显示器和一台处理器为用户提供功能并部署在计算机桌面的应用程序，常见的有 操作系统 (Windows&#x2F;Mac&#x2F;Linux)、文件管理器、数据库工具、音乐播放器、计算器等。传统开发方案有 VB 拖拽式开发、C++ duilib (微信和爱奇艺)、Java swing&#x2F;javaFx (跨平台)、C++ MFC&#x2F;Qt (开发 Window 应用)、Objective-c&#x2F;swift-cocoa (开发 Mac 应用)。</p>
</blockquote>
<h2 id="跨平台框架"><a href="#跨平台框架" class="headerlink" title="跨平台框架"></a>跨平台框架</h2><ul>
<li>NW.js：基于 Chromium (渲染)、NodeJS (后端) 但通过合并事件循环源码实现通信而耦合性高，而且和 Electron 一样体积大和占用资源高(不推荐)，经典案例有微信&#x2F;京东小程序 IDE。</li>
<li>Electron：基于 Chromium、NodeJS 通过独立线程通信，需要性能优化，案例有 VSCode。</li>
<li>Flutter：通过 Dart 语言实现基于 Skia 的高性能渲染，但需要原生开发。</li>
<li>Wails：基于 Go 语言实现高性能的轻量应用，可看作 Electron 替代品。</li>
<li>Tauri：通过 Rust 语言构建体积小、性能高、安全性强的二进制文件。</li>
</ul>
  <div align="center">
      <img src="" data-original="/images/web/tauri.png" alt="相关配置" width="100%">
  </div>

<h2 id="应用开发"><a href="#应用开发" class="headerlink" title="应用开发"></a>应用开发</h2>  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// NW.js：配置应用名、入口等信息的文件 package.json</span></span><br><span class="line">npm install -g nw</span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">&quot;name&quot;</span>: <span class="string">&quot;hello-world-nwjs&quot;</span>,</span><br><span class="line">  <span class="string">&quot;main&quot;</span>: <span class="string">&quot;index.html&quot;</span>,</span><br><span class="line">  <span class="string">&quot;version&quot;</span>: <span class="string">&quot;1.0.0&quot;</span></span><br><span class="line">&#125;</span><br><span class="line">nw  <span class="comment">// 运行命令</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// Electron：package.json、main.js</span></span><br><span class="line">npm install -g electron</span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">&quot;name&quot;</span> : <span class="string">&quot;hello-world&quot;</span>,</span><br><span class="line">  <span class="string">&quot;version&quot;</span> : <span class="string">&quot;1.0.0&quot;</span>,</span><br><span class="line">  <span class="string">&quot;main&quot;</span> : <span class="string">&quot;main.js&quot;</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta">&#x27;use strict&#x27;</span>;</span><br><span class="line"><span class="keyword">const</span> electron = <span class="built_in">require</span>(<span class="string">&#x27;electron&#x27;</span>);</span><br><span class="line"><span class="keyword">const</span> app = electron.<span class="property">app</span>;                        <span class="comment">// 应用对象的引用 </span></span><br><span class="line"><span class="keyword">const</span> <span class="title class_">BrowserWindow</span> = electron.<span class="property">BrowserWindow</span>;    <span class="comment">// 窗口对象类的引用</span></span><br><span class="line"><span class="keyword">let</span> mainWindow = <span class="literal">null</span>;                           <span class="comment">// 存储窗口对象</span></span><br><span class="line">app.<span class="title function_">on</span>(<span class="string">&#x27;window-all-closed&#x27;</span>, <span class="function">() =&gt;</span> &#123;        <span class="comment">// 应用视窗全部关闭时不触发 MacOS</span></span><br><span class="line">    <span class="keyword">if</span> (process.<span class="property">platform</span> !== <span class="string">&#x27;darwin&#x27;</span>) app.<span class="title function_">quit</span>();</span><br><span class="line">&#125;);</span><br><span class="line">app.<span class="title function_">on</span>(<span class="string">&#x27;ready&#x27;</span>, <span class="function">() =&gt;</span> &#123;</span><br><span class="line">    mainWindow = <span class="keyword">new</span> <span class="title class_">BrowserWindow</span>();  <span class="comment">// 新窗口，放变量而防止被 Node.js 回收</span></span><br><span class="line">    mainWindow.<span class="title function_">loadURL</span>(<span class="string">`file://<span class="subst">$&#123;__dirname&#125;</span>/index.html`</span>); <span class="comment">// 视窗路径：html</span></span><br><span class="line">    mainWindow.<span class="title function_">on</span>(<span class="string">&#x27;closed&#x27;</span>, <span class="function">() =&gt;</span> &#123; mainWindow = <span class="literal">null</span>; &#125;); <span class="comment">// 页面关闭后释放</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">electron .   <span class="comment">// 运行：注意有个点</span></span><br></pre></td></tr></table></figure>


<h1 id="四、Web-监控平台"><a href="#四、Web-监控平台" class="headerlink" title="四、Web 监控平台"></a>四、Web 监控平台</h1><blockquote>
<p>监控目标有：<code>用户体验</code>、<code>稳定性</code> (js 报错 &#x2F; 资源加载异常 &#x2F; 接口异常 &#x2F; 页面白屏)、<code>业务信息</code> (pv 页面浏览量和点击量 &#x2F; uv 访问站点的不同ip的人数 &#x2F; 用户在每一个页面的停留时间)。主要流程为：<code>前端埋点</code> (针对用户行为或事件进行捕获和处理)、<code>数据采集</code>、<code>数据上报</code> (上报给日志系统或存储于数据库)、<code>数据统计</code>、<code>数据可视化展示或报警</code>。</p>
</blockquote>
<h2 id="数据采集"><a href="#数据采集" class="headerlink" title="数据采集"></a>数据采集</h2>  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 1、资源加载错误 + js 执行错误：存在 e.target.src(href) 则为资源加载错误</span></span><br><span class="line"><span class="variable language_">window</span>.<span class="title function_">addEventListener</span>(<span class="string">&quot;error&quot;</span>, <span class="keyword">function</span> (<span class="params">event</span>) &#123;</span><br><span class="line">    <span class="keyword">const</span> formatTime = <span class="keyword">function</span> (<span class="params">time</span>) =&gt; <span class="keyword">new</span> <span class="title class_">Date</span>(time).<span class="title function_">getTime</span>();</span><br><span class="line">    <span class="keyword">if</span> (event.<span class="property">target</span> &amp;&amp; (event.<span class="property">target</span>.<span class="property">src</span> || event.<span class="property">target</span>.<span class="property">href</span>)) &#123;</span><br><span class="line">        tracker.<span class="title function_">send</span>(&#123;</span><br><span class="line">            <span class="attr">kind</span>: <span class="string">&quot;stability&quot;</span>,            <span class="comment">// 监控目标：稳定性</span></span><br><span class="line">            <span class="attr">type</span>: <span class="string">&quot;error&quot;</span>,                <span class="comment">// 数据类型</span></span><br><span class="line">            <span class="attr">errorType</span>: <span class="string">&quot;resourceError&quot;</span>,   <span class="comment">// 错误类型</span></span><br><span class="line">            <span class="attr">filename</span>: event.<span class="property">target</span>.<span class="property">src</span> || event.<span class="property">target</span>.<span class="property">href</span>, <span class="comment">// 加载资源</span></span><br><span class="line">            <span class="attr">tagName</span>: event.<span class="property">target</span>.<span class="property">tagName</span>,           <span class="comment">// 标签名</span></span><br><span class="line">            <span class="attr">timeStamp</span>: <span class="title function_">formatTime</span>(event.<span class="property">timeStamp</span>),  <span class="comment">// 时间</span></span><br><span class="line">            <span class="attr">selector</span>: <span class="title function_">getSelector</span>(event.<span class="property">path</span> || event.<span class="property">target</span>) <span class="comment">// 选择器</span></span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;  <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">let</span> lastEvent = <span class="title function_">getLastEvent</span>();</span><br><span class="line">        <span class="keyword">let</span> target = lastEvent &amp;&amp; (lastEvent.<span class="property">path</span> || lastEvent.<span class="property">target</span>)</span><br><span class="line">        tracker.<span class="title function_">send</span>(&#123;</span><br><span class="line">            <span class="attr">kind</span>: <span class="string">&quot;stability&quot;</span>,</span><br><span class="line">            <span class="attr">type</span>: <span class="string">&quot;error&quot;</span>,  </span><br><span class="line">            <span class="attr">errorType</span>: <span class="string">&quot;jsError&quot;</span>, </span><br><span class="line">            <span class="attr">message</span>: event.<span class="property">message</span>,               <span class="comment">// 报错信息</span></span><br><span class="line">            <span class="attr">filename</span>: event.<span class="property">filename</span>,             <span class="comment">// 报错链接</span></span><br><span class="line">            <span class="attr">position</span>: (event.<span class="property">lineNo</span> || <span class="number">0</span>) + <span class="string">&quot;:&quot;</span> + (event.<span class="property">columnNo</span> || <span class="number">0</span>),</span><br><span class="line">            <span class="attr">stack</span>: <span class="title function_">getLines</span>(event.<span class="property">error</span>.<span class="property">stack</span>),   <span class="comment">// 错误堆栈</span></span><br><span class="line">            <span class="attr">selector</span>: lastEvent ? <span class="title function_">getSelector</span>(target) : <span class="string">&quot;&quot;</span>  <span class="comment">// CSS 选择器</span></span><br><span class="line">        &#125;);</span><br><span class="line">      &#125;</span><br><span class="line">&#125;, <span class="literal">true</span>)   <span class="comment">// true 在捕获阶段调用，false 在冒泡阶段捕获</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">lastEvent</span> () &#123;</span><br><span class="line">    <span class="keyword">let</span> lastEvent;</span><br><span class="line">    [<span class="string">&quot;click&quot;</span>, <span class="string">&quot;touchstart&quot;</span>, <span class="string">&quot;mousedown&quot;</span>, <span class="string">&quot;keydown&quot;</span>, <span class="string">&quot;mouseover&quot;</span>].<span class="title function_">forEach</span>(</span><br><span class="line">    <span class="function"><span class="params">eventType</span> =&gt;</span> &#123;</span><br><span class="line">        <span class="comment">// capture 捕获或冒泡阶段执行、passive 默认不阻止默认事件</span></span><br><span class="line">        <span class="variable language_">document</span>.<span class="title function_">addEventListener</span>(eventType, <span class="function"><span class="params">event</span> =&gt;</span> &#123;</span><br><span class="line">            lastEvent = event;</span><br><span class="line">        &#125;, &#123; <span class="attr">capture</span>: <span class="literal">true</span>, <span class="attr">passive</span>: <span class="literal">true</span> &#125;);</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="keyword">return</span> lastEvent</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 2、未捕获的 promise 异常</span></span><br><span class="line"><span class="variable language_">window</span>.<span class="title function_">addEventListener</span>(<span class="string">&quot;unhandledrejection&quot;</span>, <span class="keyword">function</span> (<span class="params">event</span>) &#123;</span><br><span class="line">    <span class="keyword">let</span> lastEvent = <span class="title function_">getLastEvent</span>();</span><br><span class="line">    <span class="keyword">let</span> message = <span class="string">&quot;&quot;</span>;</span><br><span class="line">    <span class="keyword">let</span> line = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">let</span> column = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">let</span> file = <span class="string">&quot;&quot;</span>;</span><br><span class="line">    <span class="keyword">let</span> stack = <span class="string">&quot;&quot;</span>;</span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">typeof</span> event.<span class="property">reason</span> === <span class="string">&quot;string&quot;</span>) &#123;</span><br><span class="line">        message = event.<span class="property">reason</span>;</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (<span class="keyword">typeof</span> event.<span class="property">reason</span> === <span class="string">&quot;object&quot;</span>) &#123;</span><br><span class="line">        message = event.<span class="property">reason</span>.<span class="property">message</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">let</span> reason = event.<span class="property">reason</span>;</span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">typeof</span> reason === <span class="string">&quot;object&quot;</span> &amp;&amp; reason.<span class="property">stack</span>) &#123;</span><br><span class="line">        <span class="keyword">var</span> matchResult = reason.<span class="property">stack</span>.<span class="title function_">match</span>(<span class="regexp">/at\s+(.+):(\d+):(\d+)/</span>);</span><br><span class="line">        <span class="keyword">if</span> (matchResult) &#123;</span><br><span class="line">            file = matchResult[<span class="number">1</span>];</span><br><span class="line">            line = matchResult[<span class="number">2</span>];</span><br><span class="line">            column = matchResult[<span class="number">3</span>];</span><br><span class="line">        &#125;</span><br><span class="line">        stack = <span class="title function_">getLines</span>(reason.<span class="property">stack</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">let</span> target = lastEvent &amp;&amp; (lastEvent.<span class="property">path</span> || lastEvent.<span class="property">target</span>)</span><br><span class="line">    tracker.<span class="title function_">send</span>(&#123;</span><br><span class="line">        <span class="attr">kind</span>: <span class="string">&quot;stability&quot;</span>,</span><br><span class="line">        <span class="attr">type</span>: <span class="string">&quot;error&quot;</span>,</span><br><span class="line">        <span class="attr">errorType</span>: <span class="string">&quot;promiseError&quot;</span>,</span><br><span class="line">        <span class="attr">message</span>: message,</span><br><span class="line">        <span class="attr">filename</span>: file,</span><br><span class="line">        stack,</span><br><span class="line">        <span class="attr">position</span>: line + <span class="string">&quot;:&quot;</span> + column,</span><br><span class="line">        <span class="attr">selector</span>: lastEvent ? <span class="title function_">getSelector</span>(target): <span class="string">&quot;&quot;</span></span><br><span class="line">    &#125;);</span><br><span class="line">&#125;, <span class="literal">true</span>);</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">getLines</span>(<span class="params">stack</span>) &#123;</span><br><span class="line">  <span class="keyword">return</span> stack</span><br><span class="line">        .<span class="title function_">split</span>(<span class="string">&quot;\n&quot;</span>)</span><br><span class="line">        .<span class="title function_">slice</span>(<span class="number">1</span>)</span><br><span class="line">        .<span class="title function_">map</span>(<span class="function">(<span class="params">item</span>) =&gt;</span> item.<span class="title function_">replace</span>(<span class="regexp">/^\s+at\s+/g</span>, <span class="string">&quot;&quot;</span>))</span><br><span class="line">        .<span class="title function_">join</span>(<span class="string">&quot;^&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 3、接口异常：使用 webpack devServer 模拟请求</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">function</span> <span class="title function_">injectXHR</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">let</span> <span class="title class_">XMLHttpRequest</span> = <span class="variable language_">window</span>.<span class="property">XMLHttpRequest</span>;</span><br><span class="line">    <span class="keyword">let</span> oldOpen = <span class="title class_">XMLHttpRequest</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">open</span>;</span><br><span class="line">    <span class="title class_">XMLHttpRequest</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">open</span> = <span class="keyword">function</span> (<span class="params">method, url,</span></span><br><span class="line"><span class="params">    <span class="keyword">async</span>, username, password</span>) &#123;</span><br><span class="line">        <span class="comment">// 上报接口不用处理</span></span><br><span class="line">        <span class="keyword">if</span> (!url.<span class="title function_">match</span>(<span class="regexp">/logstores/</span>) &amp;&amp; !url.<span class="title function_">match</span>(<span class="regexp">/sockjs/</span>)) &#123;</span><br><span class="line">            <span class="variable language_">this</span>.<span class="property">logData</span> = &#123; method, url, <span class="keyword">async</span>, username, password &#125;;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> oldOpen.<span class="title function_">apply</span>(<span class="variable language_">this</span>, <span class="variable language_">arguments</span>);</span><br><span class="line">    &#125;;</span><br><span class="line">    <span class="keyword">let</span> start;</span><br><span class="line">    <span class="keyword">let</span> oldSend = <span class="title class_">XMLHttpRequest</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">send</span>;</span><br><span class="line">    <span class="title class_">XMLHttpRequest</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">send</span> = <span class="keyword">function</span> (<span class="params">body</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="variable language_">this</span>.<span class="property">logData</span>) &#123;</span><br><span class="line">            oldSend.<span class="title function_">apply</span>(<span class="variable language_">this</span>, <span class="variable language_">arguments</span>);</span><br><span class="line">            <span class="keyword">return</span></span><br><span class="line">        &#125;</span><br><span class="line">        start = <span class="title class_">Date</span>.<span class="title function_">now</span>();</span><br><span class="line">        <span class="keyword">let</span> <span class="title function_">handler</span> = type =&gt; <span class="function"><span class="params">event</span> =&gt;</span> &#123;</span><br><span class="line">            <span class="keyword">let</span> duration = <span class="title class_">Date</span>.<span class="title function_">now</span>() - start;</span><br><span class="line">            <span class="keyword">let</span> status = <span class="variable language_">this</span>.<span class="property">status</span>;</span><br><span class="line">            <span class="keyword">let</span> statusText = <span class="variable language_">this</span>.<span class="property">statusText</span>;</span><br><span class="line">            <span class="keyword">let</span> res = <span class="variable language_">this</span>.<span class="property">response</span> &amp;&amp; <span class="title class_">JSON</span>.<span class="title function_">stringify</span>(<span class="variable language_">this</span>.<span class="property">response</span>)</span><br><span class="line">            tracker.<span class="title function_">send</span>(&#123;</span><br><span class="line">                <span class="attr">kind</span>: <span class="string">&quot;stability&quot;</span>,</span><br><span class="line">                <span class="attr">type</span>: <span class="string">&quot;xhr&quot;</span>,</span><br><span class="line">                <span class="attr">eventType</span>: type,    <span class="comment">// load error abort</span></span><br><span class="line">                <span class="attr">pathname</span>: <span class="variable language_">this</span>.<span class="property">logData</span>.<span class="property">url</span>,  <span class="comment">// 接口地址</span></span><br><span class="line">                <span class="attr">status</span>: status + <span class="string">&quot;-&quot;</span> + statusText,</span><br><span class="line">                <span class="attr">duration</span>: <span class="string">&quot;&quot;</span> + duration,    <span class="comment">// 接口耗时</span></span><br><span class="line">                <span class="attr">response</span>: res || <span class="string">&quot;&quot;</span>,</span><br><span class="line">                <span class="attr">params</span>: body || <span class="string">&quot;&quot;</span>,</span><br><span class="line">            &#125;);</span><br><span class="line">        &#125;;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="title function_">addEventListener</span>(<span class="string">&quot;load&quot;</span>, <span class="title function_">handler</span>(<span class="string">&quot;load&quot;</span>), <span class="literal">false</span>);</span><br><span class="line">        <span class="variable language_">this</span>.<span class="title function_">addEventListener</span>(<span class="string">&quot;error&quot;</span>, <span class="title function_">handler</span>(<span class="string">&quot;error&quot;</span>), <span class="literal">false</span>);</span><br><span class="line">        <span class="variable language_">this</span>.<span class="title function_">addEventListener</span>(<span class="string">&quot;abort&quot;</span>, <span class="title function_">handler</span>(<span class="string">&quot;abort&quot;</span>), <span class="literal">false</span>);</span><br><span class="line">        oldSend.<span class="title function_">apply</span>(<span class="variable language_">this</span>, <span class="variable language_">arguments</span>);</span><br><span class="line">    &#125;;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@title</span> 4、页面白屏</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> elementsFromPoint：获取屏幕水平中线和竖直中线所在的所有元素</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> window.innerWidth/innerHeight：去除工具条与滚动条的窗口宽度和高度</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> screen.width/height：屏幕的宽度和高度</span></span><br><span class="line"><span class="comment">**/</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">getSelector</span>(<span class="params">element</span>) &#123;</span><br><span class="line">    <span class="keyword">var</span> selector;</span><br><span class="line">    <span class="keyword">var</span> className = element.<span class="property">className</span></span><br><span class="line">    <span class="keyword">if</span> (element.<span class="property">id</span>) &#123;</span><br><span class="line">        selector = <span class="string">`#<span class="subst">$&#123;element.id&#125;</span>`</span>;</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (className &amp;&amp; <span class="keyword">typeof</span> className == <span class="string">&quot;string&quot;</span>) &#123;</span><br><span class="line">        <span class="keyword">let</span> eleList = className.<span class="title function_">split</span>(<span class="string">&#x27; &#x27;</span>);</span><br><span class="line">        selector = <span class="string">&quot;.&quot;</span> + eleList.<span class="title function_">filter</span>(<span class="function"><span class="params">item</span> =&gt;</span> item).<span class="title function_">join</span>(<span class="string">&quot;.&quot;</span>);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        selector = element.<span class="property">nodeName</span>.<span class="title function_">toLowerCase</span>();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> selector;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">function</span> <span class="title function_">blankScreen</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">const</span> wrapperSelectors = [<span class="string">&#x27;body&#x27;</span>, <span class="string">&#x27;html&#x27;</span>, <span class="string">&#x27;#container&#x27;</span>, <span class="string">&#x27;.content&#x27;</span>];</span><br><span class="line">    <span class="keyword">let</span> emptyPoints = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">function</span> <span class="title function_">isWrapper</span>(<span class="params">element</span>) &#123;</span><br><span class="line">        <span class="keyword">let</span> selector = <span class="title function_">getSelector</span>(element);</span><br><span class="line">        <span class="keyword">if</span> (wrapperSelectors.<span class="title function_">indexOf</span>(selector) &gt;= <span class="number">0</span>) &#123;</span><br><span class="line">            emptyPoints++;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="title function_">onload</span>(<span class="keyword">function</span> (<span class="params"></span>) &#123;</span><br><span class="line">        <span class="keyword">let</span> xElements, yElements;</span><br><span class="line">        <span class="keyword">debugger</span>;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">1</span>; i &lt;= <span class="number">9</span>; i++) &#123;</span><br><span class="line">            xElements = <span class="variable language_">document</span>.<span class="title function_">elementsFromPoint</span>(</span><br><span class="line">              (<span class="variable language_">window</span>.<span class="property">innerWidth</span> * i) / <span class="number">10</span>,</span><br><span class="line">              <span class="variable language_">window</span>.<span class="property">innerHeight</span> / <span class="number">2</span></span><br><span class="line">            );</span><br><span class="line">            yElements = <span class="variable language_">document</span>.<span class="title function_">elementsFromPoint</span>(</span><br><span class="line">                <span class="variable language_">window</span>.<span class="property">innerWidth</span> / <span class="number">2</span>,</span><br><span class="line">                (<span class="variable language_">window</span>.<span class="property">innerHeight</span> * i) / <span class="number">10</span></span><br><span class="line">            );</span><br><span class="line">            <span class="title function_">isWrapper</span>(xElements[<span class="number">0</span>]);</span><br><span class="line">            <span class="title function_">isWrapper</span>(yElements[<span class="number">0</span>]);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (emptyPoints &gt;= <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">let</span> centerElements = <span class="variable language_">document</span>.<span class="title function_">elementsFromPoint</span>(</span><br><span class="line">                <span class="variable language_">window</span>.<span class="property">innerWidth</span> / <span class="number">2</span>,</span><br><span class="line">                <span class="variable language_">window</span>.<span class="property">innerHeight</span> / <span class="number">2</span></span><br><span class="line">            );</span><br><span class="line">            tracker.<span class="title function_">send</span>(&#123;</span><br><span class="line">                <span class="attr">kind</span>: <span class="string">&quot;stability&quot;</span>,</span><br><span class="line">                <span class="attr">type</span>: <span class="string">&quot;blank&quot;</span>,</span><br><span class="line">                <span class="attr">emptyPoints</span>: <span class="string">&quot;&quot;</span> + emptyPoints,</span><br><span class="line">                <span class="attr">screen</span>: <span class="string">`<span class="subst">$&#123;<span class="variable language_">window</span>.screen.width&#125;</span>x<span class="subst">$&#123;<span class="variable language_">window</span>.screen.height&#125;</span>`</span>,</span><br><span class="line">                <span class="attr">viewPoint</span>: <span class="string">`<span class="subst">$&#123;<span class="variable language_">window</span>.innerWidth&#125;</span>x<span class="subst">$&#123;<span class="variable language_">window</span>.innerHeight&#125;</span>`</span>,</span><br><span class="line">                <span class="attr">selector</span>: <span class="title function_">getSelector</span>(centerElements[<span class="number">0</span>])</span><br><span class="line">            &#125;);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>


  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@title</span> 5、性能监控</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> FP、FCP、LCP、CLS、FID、FMP：通过 PerformanceObserver</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> TCP 耗时、res 耗时、DOM 解析时间：通过 performance.timing</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> 长任务监听：PerformanceObserver 监听 longTask</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * <span class="doctag">@title</span> 内存泄漏</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> 产生原因：全局变量、定时器和监听器、脱离 Dom 引用、闭包</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> 开发监控：浏览器 Performance、移动端 PerformanceDog</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> 线上监控：window.performance.memory</span></span><br><span class="line"><span class="comment">**/</span> </span><br><span class="line"><span class="keyword">const</span> onload = <span class="keyword">function</span> (<span class="params">callback</span>) &#123;</span><br><span class="line">    <span class="keyword">if</span> (<span class="variable language_">document</span>.<span class="property">readyState</span> === <span class="string">&quot;complete&quot;</span>) &#123;</span><br><span class="line">        <span class="title function_">callback</span>();</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="variable language_">window</span>.<span class="title function_">addEventListener</span>(<span class="string">&quot;load&quot;</span>, callback);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">function</span> <span class="title function_">timing</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="keyword">let</span> <span class="variable constant_">FMP</span>, <span class="variable constant_">LCP</span>;</span><br><span class="line">  <span class="comment">// 增加性能条目的观察者：观察页面中有意义的元素</span></span><br><span class="line">  <span class="keyword">new</span> <span class="title class_">PerformanceObserver</span>(<span class="function">(<span class="params">entryList, observer</span>) =&gt;</span> &#123;</span><br><span class="line">      <span class="keyword">const</span> perfEntries = entryList.<span class="title function_">getEntries</span>();</span><br><span class="line">      <span class="variable constant_">FMP</span> = perfEntries[<span class="number">0</span>];</span><br><span class="line">      observer.<span class="title function_">disconnect</span>();</span><br><span class="line">  &#125;).<span class="title function_">observe</span>(&#123; <span class="attr">entryTypes</span>: [<span class="string">&quot;element&quot;</span>] &#125;);</span><br><span class="line">  <span class="comment">// 观察页面中最大的元素</span></span><br><span class="line">  <span class="keyword">new</span> <span class="title class_">PerformanceObserver</span>(<span class="function">(<span class="params">entryList, observer</span>) =&gt;</span> &#123;</span><br><span class="line">      <span class="keyword">const</span> perfEntries = entryList.<span class="title function_">getEntries</span>();</span><br><span class="line">      <span class="keyword">const</span> lastEntry = perfEntries[perfEntries.<span class="property">length</span> - <span class="number">1</span>];</span><br><span class="line">      <span class="variable constant_">LCP</span> = lastEntry;</span><br><span class="line">      observer.<span class="title function_">disconnect</span>();</span><br><span class="line">  &#125;).<span class="title function_">observe</span>(&#123; <span class="attr">entryTypes</span>: [<span class="string">&quot;largest-contentful-paint&quot;</span>] &#125;);</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">new</span> <span class="title class_">PerformanceObserver</span>(<span class="function">(<span class="params">entryList, observer</span>) =&gt;</span> &#123;</span><br><span class="line">      <span class="keyword">const</span> lastEvent = <span class="title function_">getLastEvent</span>();</span><br><span class="line">      <span class="keyword">const</span> fInput = entryList.<span class="title function_">getEntries</span>()[<span class="number">0</span>];</span><br><span class="line">      <span class="keyword">if</span> (fInput) &#123;</span><br><span class="line">          <span class="comment">// 处理延迟 = 开始处理的时间 - 开始点击的时间</span></span><br><span class="line">          <span class="keyword">let</span> inputDelay = fInput.<span class="property">processingStart</span> - fInput.<span class="property">startTime</span>;</span><br><span class="line">          <span class="keyword">let</span> duration = fInput.<span class="property">duration</span>;       <span class="comment">// 处理的耗时</span></span><br><span class="line">          <span class="keyword">let</span> target = lastEvent &amp;&amp; (lastEvent.<span class="property">path</span> || lastEvent.<span class="property">target</span>)</span><br><span class="line">          <span class="keyword">if</span> (inputDelay &gt; <span class="number">0</span> || duration &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                tracker.<span class="title function_">send</span>(&#123;</span><br><span class="line">                  <span class="attr">kind</span>: <span class="string">&quot;experience&quot;</span>,       <span class="comment">// 用户体验指标</span></span><br><span class="line">                  <span class="attr">type</span>: <span class="string">&quot;firstInputDelay&quot;</span>,  <span class="comment">// 首次输入延迟</span></span><br><span class="line">                  <span class="attr">inputDelay</span>: inputDelay ? <span class="title function_">formatTime</span>(inputDelay) : <span class="number">0</span>,</span><br><span class="line">                  <span class="attr">duration</span>: duration ? <span class="title function_">formatTime</span>(duration) : <span class="number">0</span>,</span><br><span class="line">                  <span class="attr">startTime</span>: fInput.<span class="property">startTime</span>,</span><br><span class="line">                  <span class="attr">selector</span>: lastEvent ? <span class="title function_">getSelector</span>(target) : <span class="string">&quot;&quot;</span></span><br><span class="line">              &#125;);</span><br><span class="line">          &#125;</span><br><span class="line">      &#125;</span><br><span class="line">      observer.<span class="title function_">disconnect</span>();</span><br><span class="line">  &#125;).<span class="title function_">observe</span>(&#123; <span class="attr">type</span>: <span class="string">&quot;first-input&quot;</span>, <span class="attr">buffered</span>: <span class="literal">true</span> &#125;);  <span class="comment">// 第一次交互</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// 刚开始页面内容为空，等页面渲染完成时再去判断</span></span><br><span class="line">  <span class="title function_">onload</span>(<span class="keyword">function</span> (<span class="params"></span>) &#123;</span><br><span class="line">    <span class="built_in">setTimeout</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">      <span class="keyword">const</span> &#123;</span><br><span class="line">          fetchStart,</span><br><span class="line">          connectStart,</span><br><span class="line">          connectEnd,</span><br><span class="line">          requestStart,</span><br><span class="line">          responseStart,</span><br><span class="line">          responseEnd,</span><br><span class="line">          domLoading,</span><br><span class="line">          domInteractive,</span><br><span class="line">          domContentLoadedEventStart,</span><br><span class="line">          domContentLoadedEventEnd,</span><br><span class="line">          loadEventStart</span><br><span class="line">      &#125; = <span class="variable language_">window</span>.<span class="property">performance</span>.<span class="property">timing</span>;</span><br><span class="line">      <span class="comment">// 时间指标</span></span><br><span class="line">      tracker.<span class="title function_">send</span>(&#123;</span><br><span class="line">          <span class="attr">kind</span>: <span class="string">&quot;experience&quot;</span>,   <span class="comment">// 监控目标：用户体验</span></span><br><span class="line">          <span class="attr">type</span>: <span class="string">&quot;timing&quot;</span>,       <span class="comment">// 统计每个阶段的时间</span></span><br><span class="line">          <span class="attr">connectTime</span>: connectEnd - connectStart,          <span class="comment">// TCP 连接耗时</span></span><br><span class="line">          <span class="attr">ttfbTime</span>: responseStart - requestStart,         <span class="comment">// 首字节到达时间</span></span><br><span class="line">          <span class="attr">responseTime</span>: responseEnd - responseStart,      <span class="comment">// Resp 响应耗时</span></span><br><span class="line">          <span class="attr">parseDOMTime</span>: loadEventStart - domLoading,    <span class="comment">// DOM 解析渲染耗时</span></span><br><span class="line">          <span class="attr">domContentLoadedTime</span>: domContentLoadedEventEnd - </span><br><span class="line">          domContentLoadedEventStart,         <span class="comment">// DOMContentLoaded 回调耗时</span></span><br><span class="line">          <span class="attr">timeToInteractive</span>: domInteractive - fetchStart, <span class="comment">// 首次可交互时间</span></span><br><span class="line">          <span class="attr">loadTime</span>: loadEventStart - fetchStart, <span class="comment">// 完整的加载时间</span></span><br><span class="line">      &#125;);</span><br><span class="line">      <span class="comment">// 性能指标：FP 首次绘制、FCP 首次内容绘制、FMP 首次有意义绘制、LCP 最大内容</span></span><br><span class="line">      <span class="keyword">let</span> <span class="variable constant_">FP</span> = performance.<span class="title function_">getEntriesByName</span>(<span class="string">&quot;first-paint&quot;</span>)[<span class="number">0</span>];</span><br><span class="line">      <span class="keyword">let</span> <span class="variable constant_">FCP</span>= performance.<span class="title function_">getEntriesByName</span>(<span class="string">&quot;first-contentful-paint&quot;</span>)[<span class="number">0</span>];</span><br><span class="line">      <span class="keyword">let</span> <span class="title class_">PaintTime</span> = <span class="variable constant_">LCP</span> &amp;&amp; (<span class="variable constant_">LCP</span>.<span class="property">renderTime</span> || <span class="variable constant_">LCP</span>.<span class="property">loadTime</span>)</span><br><span class="line">      tracker.<span class="title function_">send</span>(&#123;</span><br><span class="line">          <span class="attr">kind</span>: <span class="string">&quot;experience&quot;</span>,</span><br><span class="line">          <span class="attr">type</span>: <span class="string">&quot;paint&quot;</span>,</span><br><span class="line">          <span class="attr">firstPaint</span>: <span class="variable constant_">FP</span> ? <span class="title function_">formatTime</span>(<span class="variable constant_">FP</span>.<span class="property">startTime</span>) : <span class="number">0</span>,  </span><br><span class="line">          <span class="attr">firstContentPaint</span>: <span class="variable constant_">FCP</span> ? <span class="title function_">formatTime</span>(<span class="variable constant_">FCP</span>.<span class="property">startTime</span>) : <span class="number">0</span>,</span><br><span class="line">          <span class="attr">firstMeaningfulPaint</span>: <span class="variable constant_">FMP</span> ? <span class="title function_">formatTime</span>(<span class="variable constant_">FMP</span>.<span class="property">startTime</span>) : <span class="number">0</span>,</span><br><span class="line">          <span class="attr">largestContentfulPaint</span>: <span class="variable constant_">LCP</span> ? <span class="title function_">formatTime</span>(<span class="title class_">PaintTime</span>) : <span class="number">0</span></span><br><span class="line">      &#125;);</span><br><span class="line">    &#125;, <span class="number">3000</span>);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 6、卡顿：用户交互的响应时间如果大于 100ms，用户就会感觉卡顿</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">function</span> <span class="title function_">longTask</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">PerformanceObserver</span>(<span class="function"><span class="params">list</span> =&gt;</span> &#123;</span><br><span class="line">        list.<span class="title function_">getEntries</span>().<span class="title function_">forEach</span>(<span class="function"><span class="params">entry</span> =&gt;</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (entry.<span class="property">duration</span> &gt; <span class="number">100</span>) &#123;</span><br><span class="line">                <span class="keyword">let</span> lastEvent = <span class="title function_">getLastEvent</span>();</span><br><span class="line">                <span class="title function_">requestIdleCallback</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">                    tracker.<span class="title function_">send</span>(&#123;</span><br><span class="line">                        <span class="attr">kind</span>: <span class="string">&quot;experience&quot;</span>,</span><br><span class="line">                        <span class="attr">type</span>: <span class="string">&quot;longTask&quot;</span>,</span><br><span class="line">                        <span class="attr">eventType</span>: lastEvent.<span class="property">type</span>,</span><br><span class="line">                        <span class="attr">startTime</span>: <span class="title function_">formatTime</span>(entry.<span class="property">startTime</span>),</span><br><span class="line">                        <span class="attr">duration</span>: <span class="title function_">formatTime</span>(entry.<span class="property">duration</span>),</span><br><span class="line">                        <span class="attr">selector</span>: lastEvent ? <span class="title function_">getSelector</span>(lastEvent.<span class="property">path</span> </span><br><span class="line">                        || lastEvent.<span class="property">target</span>) : <span class="string">&quot;&quot;</span></span><br><span class="line">                    &#125;);</span><br><span class="line">                &#125;);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;).<span class="title function_">observe</span>(&#123; <span class="attr">entryTypes</span>: [<span class="string">&quot;longtask&quot;</span>] &#125;);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 7、数据统计：PV 页面浏览量、UV 用户访问量(一天多次访问只算一次)、用户停留时间</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">function</span> <span class="title function_">pv</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">let</span> connection = navigator.<span class="property">connection</span>;</span><br><span class="line">    <span class="keyword">let</span> screen = <span class="variable language_">window</span>.<span class="property">screen</span></span><br><span class="line">    tracker.<span class="title function_">send</span>(&#123;</span><br><span class="line">        <span class="attr">kind</span>: <span class="string">&quot;business&quot;</span>,              <span class="comment">// 监控业务</span></span><br><span class="line">        <span class="attr">type</span>: <span class="string">&quot;pv&quot;</span>,                    <span class="comment">// 监控类型</span></span><br><span class="line">        <span class="attr">rtt</span>: connection.<span class="property">rtt</span>,           <span class="comment">// 往返时间</span></span><br><span class="line">        <span class="attr">startTime</span>: performance.<span class="title function_">now</span>(),  <span class="comment">// 一个高精度的时间戳</span></span><br><span class="line">        <span class="attr">referrer</span>: <span class="variable language_">document</span>.<span class="property">referrer</span>,   <span class="comment">// 当前页面的链接网址</span></span><br><span class="line">        <span class="attr">effectiveType</span>: connection.<span class="property">effectiveType</span>,    <span class="comment">// 网络环境</span></span><br><span class="line">        <span class="attr">screen</span>: <span class="string">`<span class="subst">$&#123;screen.width&#125;</span>x<span class="subst">$&#123;screen.height&#125;</span>`</span>  <span class="comment">// 设备分辨率</span></span><br><span class="line">    &#125;);</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="keyword">let</span> startTime = <span class="title class_">Date</span>.<span class="title function_">now</span>();</span><br><span class="line">    <span class="variable language_">window</span>.<span class="title function_">addEventListener</span>(<span class="string">&quot;beforeunload&quot;</span>, <span class="function">() =&gt;</span> &#123;</span><br><span class="line">        tracker.<span class="title function_">send</span>(&#123;</span><br><span class="line">            <span class="attr">kind</span>: <span class="string">&quot;business&quot;</span>,</span><br><span class="line">            <span class="attr">type</span>: <span class="string">&quot;stayTime&quot;</span>,</span><br><span class="line">            <span class="attr">stayTime</span>: <span class="title class_">Date</span>.<span class="title function_">now</span>() - startTime</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;, <span class="literal">false</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="异常上报"><a href="#异常上报" class="headerlink" title="异常上报"></a>异常上报</h2>  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@title</span> 1、异常日志</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> createCenterBizLog 创建中央日志：保存到数据库。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> errorLog 创建错误日志文件、向日志里追加错误信息。</span></span><br><span class="line"><span class="comment">**/</span> </span><br><span class="line"><span class="keyword">function</span> <span class="title function_">centerBizLogMsg</span> (app, user, terminal, url, data) &#123;</span><br><span class="line">    <span class="keyword">let</span> isError = <span class="keyword">typeof</span> data == <span class="string">&#x27;string&#x27;</span> ? <span class="literal">true</span> : <span class="literal">false</span></span><br><span class="line">    <span class="keyword">let</span> paramLog = &#123;</span><br><span class="line">        <span class="title class_">AppID</span>: app.<span class="property">AppID</span>,</span><br><span class="line">        <span class="title class_">AppName</span>: app.<span class="property">Name</span>,</span><br><span class="line">        <span class="title class_">Terminal</span>: terminal,</span><br><span class="line">        <span class="title class_">Message</span>: isError ? data : <span class="title class_">JSON</span>.<span class="title function_">stringify</span>(data),</span><br><span class="line">        <span class="title class_">Level</span>: isError ? <span class="number">2</span> : <span class="number">3</span>, <span class="comment">// 0 信息、1 警告、2 错误、3 执行</span></span><br><span class="line">        <span class="title class_">Operator</span>: user.<span class="property">Code</span>,</span><br><span class="line">        <span class="attr">URL</span>: <span class="string">`/api/<span class="subst">$&#123;app.AppID&#125;</span>/<span class="subst">$&#123;url&#125;</span>`</span></span><br><span class="line">    &#125;;</span><br><span class="line">    centerBizLog.<span class="title function_">createCenterBizLog</span>(paramLog);</span><br><span class="line">    <span class="keyword">if</span> (isError) &#123; <span class="title class_">BaseFun</span>.<span class="title function_">errorLog</span>(<span class="string">`<span class="subst">$&#123;app.Name&#125;</span> <span class="subst">$&#123;url&#125;</span> 错误：<span class="subst">$&#123;data&#125;</span>`</span>) &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">exports</span>.<span class="property">createCenterBizLog</span> = <span class="keyword">async</span> <span class="keyword">function</span> (<span class="params">data</span>) &#123;</span><br><span class="line">    <span class="keyword">let</span> appLog = <span class="keyword">await</span> entityFun.<span class="title function_">findOne</span>(<span class="string">&#x27;AppLogController&#x27;</span>, &#123; </span><br><span class="line">        <span class="title class_">AppID</span>: data.<span class="property">AppID</span>, </span><br><span class="line">        <span class="title class_">IsLogger</span>: <span class="literal">true</span> </span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="keyword">if</span> (!appLog) &#123; <span class="keyword">return</span> &#125;</span><br><span class="line">    <span class="keyword">if</span> (data.<span class="property">Level</span> == <span class="title class_">Enum</span>.<span class="property">LevelEnum</span>.<span class="property">ERROR</span>.<span class="property">Value</span> ||</span><br><span class="line">        data.<span class="property">Level</span> == <span class="title class_">Enum</span>.<span class="property">LevelEnum</span>.<span class="property">WARNING</span>.<span class="property">Value</span> ||</span><br><span class="line">        data.<span class="property">Level</span> == <span class="title class_">Enum</span>.<span class="property">LevelEnum</span>.<span class="property">INFO</span>.<span class="property">Value</span> ||</span><br><span class="line">        data.<span class="property">Level</span> == <span class="title class_">Enum</span>.<span class="property">LevelEnum</span>.<span class="property">EXECUTE</span>.<span class="property">Value</span>) &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">let</span> log = <span class="title class_">Object</span>.<span class="title function_">assign</span>(&#123;&#125;, data, &#123;</span><br><span class="line">                <span class="title class_">CreatedOn</span>: <span class="keyword">new</span> <span class="title class_">Date</span>(),</span><br><span class="line">                <span class="title class_">CreatedBy</span>: data.<span class="property">Operator</span>,</span><br><span class="line">                <span class="title class_">RowStatus</span>: <span class="title class_">BaseEnum</span>.<span class="property">RowStatus</span>.<span class="property">Insert</span>.<span class="property">Value</span></span><br><span class="line">            &#125;)</span><br><span class="line">            entity.<span class="title function_">save</span>([&#123; <span class="title class_">EntityName</span>: <span class="string">&#x27;CenterBizLog&#x27;</span>, <span class="title class_">Records</span>: [log]&#125;]);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (error) &#123;</span><br><span class="line">            <span class="title class_">BaseFun</span>.<span class="title function_">errorLog</span>(<span class="string">&#x27;中央日志错误&#x27;</span>, error.<span class="property">message</span>, data.<span class="property">Operator</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="built_in">exports</span>.<span class="property">errorLog</span> = <span class="keyword">function</span> (<span class="params">title, msg, strUser</span>) &#123;</span><br><span class="line">    <span class="built_in">exports</span>.<span class="title function_">log</span>(<span class="title class_">BaseEnum</span>.<span class="property">LogType</span>.<span class="property">Error</span>, title, msg, strUser);</span><br><span class="line">&#125;;</span><br><span class="line"><span class="built_in">exports</span>.<span class="property">accessLog</span> = <span class="keyword">function</span> (<span class="params">title, msg, strUser</span>) &#123;</span><br><span class="line">    <span class="built_in">exports</span>.<span class="title function_">log</span>(<span class="title class_">BaseEnum</span>.<span class="property">LogType</span>.<span class="property">Access</span>, title, msg, strUser);</span><br><span class="line">&#125;;</span><br><span class="line"><span class="built_in">exports</span>.<span class="property">executeLog</span> = <span class="keyword">function</span> (<span class="params">title, msg, strUser</span>) &#123;</span><br><span class="line">    <span class="built_in">exports</span>.<span class="title function_">log</span>(<span class="title class_">BaseEnum</span>.<span class="property">LogType</span>.<span class="property">Execute</span>, title, msg, strUser);</span><br><span class="line">&#125;;</span><br><span class="line"><span class="built_in">exports</span>.<span class="property">log</span> = <span class="keyword">function</span> (<span class="params">logType, title, msg, strUser</span>) &#123;</span><br><span class="line">    <span class="keyword">var</span> nowDate = <span class="keyword">new</span> <span class="title class_">Date</span>();</span><br><span class="line">    <span class="keyword">var</span> month = nowDate.<span class="title function_">getMonth</span>() + <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">var</span> dateStr = nowDate.<span class="title function_">getDate</span>().<span class="title function_">toString</span>();</span><br><span class="line">    <span class="keyword">var</span> hoursStr = nowDate.<span class="title function_">getHours</span>().<span class="title function_">toString</span>();</span><br><span class="line">    <span class="keyword">var</span> minutesStr = nowDate.<span class="title function_">getMinutes</span>().<span class="title function_">toString</span>();</span><br><span class="line">    <span class="keyword">var</span> secondsStr = nowDate.<span class="title function_">getSeconds</span>().<span class="title function_">toString</span>();</span><br><span class="line">    <span class="keyword">var</span> fileName = nowDate.<span class="title function_">getFullYear</span>() + <span class="string">&#x27;.&#x27;</span> + </span><br><span class="line">        (month &lt; <span class="number">10</span> ? <span class="string">&#x27;0&#x27;</span> + month.<span class="title function_">toString</span>() : month.<span class="title function_">toString</span>()) +</span><br><span class="line">        <span class="string">&#x27;.&#x27;</span> + (nowDate.<span class="title function_">getDate</span>() &lt; <span class="number">10</span> ? <span class="string">&#x27;0&#x27;</span> + dateStr : dateStr);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">var</span> strMsg = (hoursStr.<span class="property">length</span> == <span class="number">1</span> ? <span class="string">&#x27;0&#x27;</span> + hoursStr : hoursStr) +</span><br><span class="line">        <span class="string">&quot;:&quot;</span> + (minutesStr.<span class="property">length</span> == <span class="number">1</span> ? <span class="string">&#x27;0&#x27;</span> + minutesStr : minutesStr) +</span><br><span class="line">        <span class="string">&quot;:&quot;</span> + (secondsStr.<span class="property">length</span> == <span class="number">1</span> ? <span class="string">&#x27;0&#x27;</span> + secondsStr : secondsStr);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (strUser != <span class="literal">undefined</span> &amp;&amp; strUser.<span class="title function_">trim</span>() != <span class="string">&#x27;&#x27;</span>) &#123;</span><br><span class="line">        strMsg += <span class="string">&#x27; 【&#x27;</span> + strUser + <span class="string">&#x27;】&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">var</span> fileFullName;</span><br><span class="line">    <span class="keyword">if</span> (logType == <span class="title class_">BaseEnum</span>.<span class="property">LogType</span>.<span class="property">Error</span>) &#123;</span><br><span class="line">        fileFullName = <span class="string">&#x27;./logs/error/&#x27;</span> + fileName + <span class="string">&#x27;.log&#x27;</span>;</span><br><span class="line">        strMsg += <span class="string">&#x27;\r\n标题：&#x27;</span> + title + <span class="string">&#x27;\r\n&#x27;</span> + <span class="string">&#x27;错误信息：&#x27;</span> + msg;</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (logType == <span class="title class_">BaseEnum</span>.<span class="property">LogType</span>.<span class="property">Access</span>) &#123;</span><br><span class="line">        fileFullName = <span class="string">&#x27;./logs/access/&#x27;</span> + fileName + <span class="string">&#x27;.log&#x27;</span>;</span><br><span class="line">        strMsg += <span class="string">&#x27;\r\n标题：&#x27;</span> + title + <span class="string">&#x27;\r\n&#x27;</span> + <span class="string">&#x27;访问信息：&#x27;</span> + msg;</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (logType == <span class="title class_">BaseEnum</span>.<span class="property">LogType</span>.<span class="property">Execute</span>) &#123;</span><br><span class="line">        fileFullName = <span class="string">&#x27;./logs/execute/&#x27;</span> + fileName + <span class="string">&#x27;.log&#x27;</span>;</span><br><span class="line">        strMsg += <span class="string">&#x27;\r\n标题：&#x27;</span> + title + <span class="string">&#x27;\r\n&#x27;</span> + <span class="string">&#x27;执行信息：&#x27;</span> + msg;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    fs.<span class="title function_">exists</span>(fileFullName, <span class="keyword">function</span> (<span class="params">isExist</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (isExist) &#123;</span><br><span class="line">            fs.<span class="title function_">appendFile</span>(fileFullName, strMsg, <span class="keyword">function</span> (<span class="params">err, data</span>) &#123;</span><br><span class="line">                <span class="keyword">if</span> (!err) &#123; <span class="keyword">return</span> &#125;</span><br><span class="line">                <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`日志追加信息出错: \r\n <span class="subst">$&#123;err.toString()&#125;</span>`</span>);</span><br><span class="line">            &#125;);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            fs.<span class="title function_">writeFile</span>(fileFullName, strMsg, <span class="keyword">function</span> (<span class="params">err, data</span>) &#123;</span><br><span class="line">                <span class="keyword">if</span> (!err) &#123; <span class="keyword">return</span> &#125;</span><br><span class="line">                <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`访问日志创建出错: \r\n <span class="subst">$&#123;err.toString()&#125;</span>`</span>);</span><br><span class="line">            &#125;);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@title</span> 2、异常上报</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> ajax 上传：引用通信库之后调用接口。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> img 标签动态创建：浏览器自动发送 get 请求，页面无需刷新且无需引用库。</span></span><br><span class="line"><span class="comment">**/</span></span><br><span class="line"><span class="variable language_">window</span>.<span class="title function_">addEventListener</span>(<span class="string">&#x27;error&#x27;</span>, <span class="function"><span class="params">args</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="title function_">uploadError</span>(args)</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;, <span class="literal">true</span>);</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">uploadError</span>(<span class="params">&#123; lineno, colno, error, timeStamp, filename &#125;</span>) &#123;</span><br><span class="line">    <span class="keyword">const</span> info = &#123;</span><br><span class="line">        lineno,</span><br><span class="line">        colno,</span><br><span class="line">        timeStamp,</span><br><span class="line">        filename,</span><br><span class="line">        <span class="attr">stack</span>: error.<span class="property">stack</span>,</span><br><span class="line">        <span class="attr">message</span>: error.<span class="property">message</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">const</span> str = <span class="variable language_">window</span>.<span class="title function_">btoa</span>(<span class="title class_">JSON</span>.<span class="title function_">stringify</span>(info))</span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">Image</span>().<span class="property">src</span> = <span class="string">`http://localhost:80/monitor/error?info=<span class="subst">$&#123;str&#125;</span>`</span></span><br><span class="line">    <span class="comment">// const str = new Buffer(JSON.stringify(info)).toString(&quot;base64&quot;);</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 3、上报日志系统：tracker.js</span></span><br><span class="line"><span class="keyword">let</span> host = <span class="string">&quot;cn-guangdong-log.aliyuncs.com&quot;</span>;   <span class="comment">// 主机</span></span><br><span class="line"><span class="keyword">let</span> project = <span class="string">&quot;yymonitor&quot;</span>;                  <span class="comment">// 项目名</span></span><br><span class="line"><span class="keyword">let</span> logstore = <span class="string">&quot;yymonitor-store&quot;</span>;           <span class="comment">// 存储名</span></span><br><span class="line"><span class="keyword">let</span> userAgent = <span class="built_in">require</span>(<span class="string">&quot;user-agent&quot;</span>);    <span class="comment">// 用户代理</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">getExtraData</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> &#123;</span><br><span class="line">        <span class="attr">title</span>: <span class="variable language_">document</span>.<span class="property">title</span>,</span><br><span class="line">        <span class="attr">url</span>: location.<span class="property">href</span>,</span><br><span class="line">        <span class="attr">timestamp</span>: <span class="title class_">Date</span>.<span class="title function_">now</span>(),</span><br><span class="line">        <span class="attr">userAgent</span>: userAgent.<span class="title function_">parse</span>(navigator.<span class="property">userAgent</span>).<span class="property">name</span></span><br><span class="line">    &#125;;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">SendTracker</span> &#123;</span><br><span class="line">    <span class="title function_">constructor</span>(<span class="params"></span>) &#123;</span><br><span class="line">        <span class="comment">// 上报路径</span></span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">url</span> = <span class="string">`http://<span class="subst">$&#123;project&#125;</span>.<span class="subst">$&#123;host&#125;</span>/logstores/<span class="subst">$&#123;logstore&#125;</span>`</span>;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">xhr</span> = <span class="keyword">new</span> <span class="title class_">XMLHttpRequest</span>();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="title function_">send</span>(<span class="params">data = &#123;&#125;</span>) &#123;</span><br><span class="line">        <span class="keyword">let</span> extraData = <span class="title function_">getExtraData</span>();</span><br><span class="line">        <span class="keyword">let</span> log = &#123; ...data, ...extraData &#125;;</span><br><span class="line">        <span class="comment">// 阿里云要求值不能为数字</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">const</span> key <span class="keyword">in</span> log) &#123;</span><br><span class="line">            <span class="keyword">if</span> (<span class="keyword">typeof</span> log[key] === <span class="string">&quot;number&quot;</span>) &#123;</span><br><span class="line">                log[key] = <span class="string">`<span class="subst">$&#123;log[key]&#125;</span>`</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;log&quot;</span>, log);</span><br><span class="line">        <span class="comment">// 接入日志系统，此处以阿里云为例</span></span><br><span class="line">        <span class="keyword">let</span> body = <span class="title class_">JSON</span>.<span class="title function_">stringify</span>(&#123; <span class="attr">__logs__</span>: [log] &#125;);</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">xhr</span>.<span class="title function_">open</span>(<span class="string">&quot;POST&quot;</span>, <span class="variable language_">this</span>.<span class="property">url</span>, <span class="literal">true</span>);</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">xhr</span>.<span class="title function_">setRequestHeader</span>(<span class="string">&quot;Content-Type&quot;</span>, <span class="string">&quot;application/json&quot;</span>);</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">xhr</span>.<span class="title function_">setRequestHeader</span>(<span class="string">&quot;x-log-apiversion&quot;</span>, <span class="string">&quot;1.0.0&quot;</span>);</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">xhr</span>.<span class="title function_">setRequestHeader</span>(<span class="string">&quot;x-log-bodyrawsize&quot;</span>, body.<span class="property">length</span>);</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">xhr</span>.<span class="property">onload</span> = <span class="keyword">function</span> (<span class="params"></span>) &#123; &#125;;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">xhr</span>.<span class="property">onerror</span> = <span class="keyword">function</span> (<span class="params">error</span>) &#123; &#125;;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">xhr</span>.<span class="title function_">send</span>(body);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">new</span> <span class="title class_">SendTracker</span>();</span><br></pre></td></tr></table></figure>


<h2 id="JS-异常"><a href="#JS-异常" class="headerlink" title="JS 异常"></a>JS 异常</h2>  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@title</span> 1、Error 错误对象：发生错误时抛出而用来终止程序。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> 主要属性：message 错误消息、name 错误类型、stack 定位错误的调用栈。</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * <span class="doctag">@title</span> JS 错误类型：预定义的构造函数，Error 派生对象。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> SyntaxError  语法错误：代码解析时发现缺少引号或括号、变量名不合法。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> ReferenceError 引用错误：比如引用的变量或模块名在当前作用域不存在。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> RangeError 范围错误：数值超过有效范围，比如数组长度为负、数字调用。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> TypeError  类型错误：变量或参数不是预期类型，比如数字不是构造函数。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> URLError   地址错误：decodeURI encodeURI escape 等参数不对。</span></span><br><span class="line"><span class="comment">**/</span></span><br><span class="line"><span class="keyword">let</span> 1a;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(a)</span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Array</span>(-<span class="number">1</span>)、<span class="number">2.</span><span class="title function_">toFixed</span>();</span><br><span class="line"><span class="keyword">new</span> <span class="number">1</span>、<span class="number">2.</span><span class="title function_">map</span>(<span class="function"><span class="params">item</span> =&gt;</span> item);</span><br><span class="line"><span class="built_in">decodeURI</span>(<span class="string">&#x27;%2&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 创建和抛出错误：new 可省略</span></span><br><span class="line"><span class="keyword">let</span> newErr = <span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">&quot;自定义错误&quot;</span>)</span><br><span class="line"><span class="keyword">throw</span> <span class="title class_">TypeError</span>(<span class="string">&quot;Expected number&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// stack</span></span><br><span class="line"><span class="string">`ReferenceError: user is not defined</span></span><br><span class="line"><span class="string">    at fun1 (http://127.0.0.1:8080/index.html:49:13)</span></span><br><span class="line"><span class="string">    at http://127.0.0.1:8080/index.html:60:13`</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@title</span> 2、JS 错误收集</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> <span class="variable">try</span>-catch：捕获同步代码的可预见异常，语法和异步错误不行。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> window.onerror：捕获后面同异步代码的意外错误，资源加载和网络请求则不行。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> window.addEventListener(&#x27;error&#x27;, fn)：捕获资源加载和网络请求的错误。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> Promise.catch：捕获 Promise 封装的网络请求、文件读取等异步代码执行错误。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> performance.getEntries：获取所有的已加载资源的信息，间接获取资源错误。</span></span><br><span class="line"><span class="comment">**/</span></span><br><span class="line"><span class="built_in">exports</span>.<span class="property">getBin</span> = <span class="keyword">async</span> <span class="keyword">function</span> (<span class="params">prames</span>) &#123;</span><br><span class="line">    <span class="keyword">let</span> app = prames.<span class="property">app</span>;</span><br><span class="line">    <span class="keyword">let</span> user = prames.<span class="property">user</span>;</span><br><span class="line">    <span class="keyword">let</span> terminal = prames.<span class="property">terminal</span>;</span><br><span class="line">    <span class="keyword">let</span> newErr = <span class="keyword">new</span> <span class="title class_">Error</span>();</span><br><span class="line">        newErr.<span class="property">leval</span> = <span class="number">3</span>;</span><br><span class="line">        newErr.<span class="property">title</span> = <span class="string">&quot;语法错误&quot;</span>;</span><br><span class="line">    <span class="keyword">try</span> &#123; </span><br><span class="line">        <span class="title function_">centerBizLogMsg</span>(app, user, terminal, <span class="string">&#x27;getBin&#x27;</span>, prames)</span><br><span class="line">        <span class="keyword">let</span> <span class="title class_">Bin</span> = <span class="keyword">await</span> entityFun.<span class="title function_">findOne</span>(<span class="string">&#x27;Bin&#x27;</span>, &#123; prames.<span class="property">Code</span> &#125;);</span><br><span class="line">        <span class="keyword">if</span> (!<span class="title class_">Bin</span>) &#123;</span><br><span class="line">            newErr.<span class="property">message</span> = <span class="string">&#x27;未查询到当前库位信息！&#x27;</span></span><br><span class="line">            <span class="keyword">throw</span> newErr</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="title class_">Bin</span></span><br><span class="line">    &#125; <span class="keyword">catch</span> (error) &#123;</span><br><span class="line">        newErr.<span class="property">message</span> = error.<span class="property">message</span></span><br><span class="line">        <span class="title function_">centerBizLogMsg</span>(app, user, terminal, <span class="string">&quot;getBin&quot;</span>, error.<span class="property">message</span>)</span><br><span class="line">        <span class="keyword">throw</span> error</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">exports</span>.<span class="property">findOne</span> = <span class="keyword">function</span>(<span class="params">entityName, condition, populate</span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="keyword">function</span>(<span class="params">resolve, reject</span>) &#123;</span><br><span class="line">        <span class="keyword">let</span> entity = entityCollection.<span class="title function_">getEntity</span>(entityName);</span><br><span class="line">        entity.<span class="title function_">findOne</span>(condition, populate, <span class="keyword">function</span>(<span class="params">err, record</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (err) &#123;</span><br><span class="line">                <span class="keyword">let</span> newErr = <span class="keyword">new</span> <span class="title class_">Error</span>();</span><br><span class="line">                newErr.<span class="property">leval</span> = <span class="number">9</span>;</span><br><span class="line">                newErr.<span class="property">title</span> = <span class="string">&#x27;查询错误&#x27;</span> + entityName;</span><br><span class="line">                newErr.<span class="property">message</span> = <span class="string">&#x27;语法错误，请检查查询参数&#x27;</span> + err.<span class="property">message</span>;</span><br><span class="line">                <span class="title function_">reject</span>(newErr);</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="title function_">resolve</span>(record);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="variable language_">window</span>.<span class="property">onerror</span> = <span class="keyword">function</span> (<span class="params">msg, url, row, col, error</span>) &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;错误信息&#x27;</span>, <span class="string">&#x27;出错文件&#x27;</span>, <span class="string">&#x27;行号&#x27;</span>, <span class="string">&#x27;列号&#x27;</span>, <span class="string">&#x27;详细信息&#x27;</span>)</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">true</span>;    <span class="comment">// 返回 true 时控制台不显示错误信息</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="variable language_">window</span>.<span class="title function_">addEventListener</span>(<span class="string">&#x27;error&#x27;</span>, <span class="function"><span class="params">error</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;img src=&quot;../assets/test.png&quot; 加载异常&#x27;</span>, error);</span><br><span class="line">&#125;, <span class="literal">true</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="title class_">Promise</span>.<span class="title function_">reject</span>(<span class="string">&#x27;promise error&#x27;</span>)</span><br><span class="line"><span class="variable language_">window</span>.<span class="title function_">addEventListener</span>(<span class="string">&quot;unhandledrejection&quot;</span>, <span class="keyword">function</span>(<span class="params">e</span>)&#123;</span><br><span class="line">    e.<span class="title function_">preventDefault</span>()   <span class="comment">// 用于去掉控制台的异常显示信息</span></span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;全局监听 Uncaught Promise Error：&#x27;</span>, e);</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">performance.<span class="title function_">getEntries</span>().<span class="title function_">forEach</span>(<span class="function"><span class="params">item</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(item.<span class="property">name</span>)</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@title</span> 3、框架错误收集</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> JS 脚本跨域异常：通过 script 标签添加 crossOrigin 属性。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> Vue 捕获组件错误：errorHandler 全局捕获、errorCaptured 子孙组件抛出。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> React 异常捕获：内置函数 componentDidCatch、错误边界 ErrorBoundary。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> Webpack 定位：sourceMap 存储转换代码前后的位置信息，常用于分析异常代码。</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * <span class="doctag">@title</span> 实时错误追踪的开源框架：实时提供受影响的用户数量、调用堆栈、浏览器等。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> Sentry：专注于应用崩溃而非信息错误，支持集成 JS 框架、GitHub 服务等。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> Rollbar：用于前后端的无代理错误监控工具，提供了人工智能辅助的工作流程。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> Fundebug：专注于 JS、小程序、React Native、NodeJS、Java 线上监控。</span></span><br><span class="line"><span class="comment">**/</span></span><br><span class="line"><span class="comment">// Vue：main.js 全局监听错误、子组件抛出错误</span></span><br><span class="line"><span class="title class_">Vue</span>.<span class="property">config</span>.<span class="property">errorHandler</span> = <span class="function">(<span class="params">err, vm, info</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">error</span>(err, vm, info);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">const</span> error = <span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">&#x27;test error&#x27;</span>);</span><br><span class="line">error.<span class="property">code</span> = -<span class="number">1</span>;</span><br><span class="line"><span class="keyword">throw</span> error;</span><br><span class="line"></span><br><span class="line"><span class="language-xml"><span class="tag">&lt;<span class="name">template</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">    <span class="tag">&lt;<span class="name">span</span> <span class="attr">id</span>=<span class="string">&quot;count&quot;</span>&gt;</span>&#123;&#123; count &#125;&#125;<span class="tag">&lt;/<span class="name">span</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">    <span class="tag">&lt;<span class="name">test</span> /&gt;</span></span></span><br><span class="line"><span class="language-xml"><span class="tag">&lt;/<span class="name">template</span>&gt;</span></span></span><br><span class="line"><span class="language-xml"><span class="tag">&lt;<span class="name">script</span> <span class="attr">setup</span>&gt;</span><span class="language-javascript"></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml">    <span class="keyword">import</span> &#123; defineComponent, onErrorCaptured, ref &#125; <span class="keyword">from</span> <span class="string">&#x27;vue&#x27;</span></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml">    <span class="keyword">import</span> test <span class="keyword">from</span> <span class="string">&#x27;./test.vue&#x27;</span></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml">    <span class="keyword">const</span> count = <span class="title function_">ref</span>(<span class="number">0</span>)</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml">    <span class="title function_">defineComponent</span>(&#123; test &#125;)</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml">    <span class="title function_">onErrorCaptured</span>(<span class="function"><span class="params">err</span> =&gt;</span> &#123;</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml">        ++count.<span class="property">value</span></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;Caught error&#x27;</span>, err.<span class="property">message</span>)</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml">        <span class="keyword">return</span> <span class="literal">false</span>        <span class="comment">// 停止该错误继续冒泡</span></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml">    &#125;)</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="language-xml"><span class="tag">&lt;<span class="name">template</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">    <span class="tag">&lt;<span class="name">button</span> @<span class="attr">click</span>=<span class="string">&quot;test&quot;</span>&gt;</span>test 组件抛出错误<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span></span><br><span class="line"><span class="language-xml"><span class="tag">&lt;/<span class="name">template</span>&gt;</span></span></span><br><span class="line"><span class="language-xml"><span class="tag">&lt;<span class="name">script</span> <span class="attr">setup</span>&gt;</span><span class="language-javascript"></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml">    <span class="keyword">const</span> <span class="title function_">test</span> = <span class="keyword">async</span> (<span class="params"></span>) =&gt; &#123;</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml">        <span class="keyword">await</span> <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="function"><span class="params">resolve</span> =&gt;</span> <span class="built_in">setTimeout</span>(resolve, <span class="number">50</span>))</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">&#x27;Oops!&#x27;</span>)</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml">    &#125;</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@title</span> React 错误边界：只能捕获其子组件的错误</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> 使用方式：&lt;ErrorBoundary&gt; &lt;MyCom /&gt; &lt;/ErrorBoundary&gt;</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> 解决问题：默认时如果一个组件渲染时发生错误会导致整个组件树都被卸载。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> 错误边界：一个捕获到子组件错误时不渲染而展示降级组件的 React 组件。</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * <span class="doctag">@title</span> PureComponent：通过比较 prop、state 而实现渲染优化的纯组件。</span></span><br><span class="line"><span class="comment"> **/</span></span><br><span class="line"><span class="keyword">import</span> <span class="title class_">ErrorBoundary</span> <span class="keyword">from</span> <span class="string">&#x27;./ErrorBoundary&#x27;</span></span><br><span class="line"><span class="title class_">ReactDOM</span>.<span class="title function_">render</span>(</span><br><span class="line">  <span class="language-xml"><span class="tag">&lt;<span class="name">ErrorBoundary</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">      <span class="tag">&lt;<span class="name">App</span> /&gt;</span></span></span><br><span class="line"><span class="language-xml">  <span class="tag">&lt;/<span class="name">ErrorBoundary</span>&gt;</span></span></span><br><span class="line">, <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&#x27;root&#x27;</span>));</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="title class_">ErrorPage</span> <span class="keyword">from</span>  ../<span class="title class_">ErrorPage</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="title class_">React</span>, &#123; <span class="title class_">PureComponent</span> &#125; <span class="keyword">from</span> react;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">class</span> <span class="title class_">ErrorBoundary</span> <span class="keyword">extends</span> <span class="title class_ inherited__">PureComponent</span> &#123;</span><br><span class="line">    <span class="title function_">constructor</span>(<span class="params">props</span>) &#123;</span><br><span class="line">        <span class="variable language_">super</span>(props);</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">state</span> = &#123; <span class="attr">hasError</span>: <span class="literal">false</span> &#125;;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 子组件错误时才运行的静态函数：更新 state 使下次渲染显示降级组件</span></span><br><span class="line">    <span class="keyword">static</span> <span class="title function_">getDerivedStateFromError</span>(<span class="params">error</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> &#123; <span class="attr">hasError</span>: <span class="literal">true</span> &#125;;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 存储错误消息的生命周期</span></span><br><span class="line">    <span class="title function_">componentDidCatch</span>(<span class="params">error, errorInfo</span>) &#123;</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;错误信息和错误堆栈&#x27;</span>, error, errorInfo);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="title function_">render</span>(<span class="params"></span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="variable language_">this</span>.<span class="property">state</span>.<span class="property">hasError</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="language-xml"><span class="tag">&lt;<span class="name">ErrorPage</span> /&gt;</span></span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">props</span>.<span class="property">children</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 前端 script 加 crossorigin、后端配置 Access-Control-Allow-Origin</span></span><br><span class="line">&lt;script src=<span class="string">&quot;http://localhost:3000/main.js&quot;</span> crossorigin&gt;&lt;/script&gt;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> server = http.<span class="title function_">createServer</span>(<span class="function">(<span class="params">req,res</span>)=&gt;</span>&#123;</span><br><span class="line">    res.<span class="title function_">setHeader</span>(<span class="string">&quot;Access-Control-Allow-Origin&quot;</span>, <span class="string">&quot;*&quot;</span>);</span><br><span class="line">&#125;).<span class="title function_">listen</span>(<span class="number">3000</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> express = <span class="built_in">require</span>(<span class="string">&#x27;express&#x27;</span>)</span><br><span class="line"><span class="keyword">const</span> cors = <span class="built_in">require</span>(<span class="string">&#x27;cors&#x27;</span>)</span><br><span class="line"><span class="keyword">const</span> app = <span class="title function_">express</span>()</span><br><span class="line">app.<span class="title function_">use</span>(<span class="title function_">cors</span>())</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">(<span class="keyword">function</span>(<span class="params">w</span>) &#123;</span><br><span class="line">    w.<span class="property">_error_store</span> = [];</span><br><span class="line">    <span class="keyword">function</span> <span class="title function_">errhandler</span>(<span class="params"></span>)&#123;           </span><br><span class="line">        w.<span class="property">_error_store</span> &amp;&amp; w.<span class="property">_error_store</span>.<span class="title function_">push</span>([].<span class="property">slice</span>.<span class="title function_">call</span>(<span class="variable language_">arguments</span>));</span><br><span class="line">    &#125; </span><br><span class="line">    w.<span class="property">addEventListener</span> &amp;&amp; w.<span class="title function_">addEventListener</span>(<span class="string">&quot;error&quot;</span>, errhandler, <span class="literal">true</span>);</span><br><span class="line">    <span class="keyword">var</span> times = <span class="number">3</span>,</span><br><span class="line">    appendScript = <span class="keyword">function</span> <span class="title function_">appendScript</span>(<span class="params"></span>) &#123;</span><br><span class="line">        <span class="keyword">var</span> sc = <span class="variable language_">document</span>.<span class="title function_">createElement</span>(<span class="string">&quot;script&quot;</span>);</span><br><span class="line">        sc.<span class="property">async</span> = <span class="literal">true</span>,</span><br><span class="line">        sc.<span class="property">src</span> = <span class="string">&#x27;./build/skyeye.js&#x27;</span>,</span><br><span class="line">        sc.<span class="property">crossOrigin</span> = <span class="string">&quot;anonymous&quot;</span>,</span><br><span class="line">        sc.<span class="property">onerror</span> = <span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">            times--,</span><br><span class="line">            times &gt; <span class="number">0</span> &amp;&amp; <span class="built_in">setTimeout</span>(appendScript, <span class="number">1500</span>)</span><br><span class="line">        &#125;,</span><br><span class="line">        <span class="variable language_">document</span>.<span class="property">head</span> &amp;&amp; <span class="variable language_">document</span>.<span class="property">head</span>.<span class="title function_">appendChild</span>(sc);</span><br><span class="line">    &#125;;</span><br><span class="line">    <span class="built_in">setTimeout</span>(appendScript, <span class="number">1500</span>);</span><br><span class="line">&#125;)(<span class="variable language_">window</span>);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@title</span> 4、NodeJS</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * <span class="doctag">@title</span> 定位错误的脚本</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> 安装依赖：npm install source-map</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> 运行脚本：node map 1 59 0.07f.js  错误行数、列数、文件</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> 脚本依赖：fs 读取文件、souceMap 处理文件、process 进程</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * <span class="doctag">@title</span> 错误处理中间件：通过中间件向服务端应用添加功能。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> 最大好处是将所有错误集中处理，可选择启用/禁用此设置进行测试。</span></span><br><span class="line"><span class="comment">**/</span></span><br><span class="line"><span class="keyword">var</span> fs = <span class="built_in">require</span>(<span class="string">&#x27;fs&#x27;</span>)  </span><br><span class="line"><span class="keyword">var</span> <span class="title class_">SourceMapConsumer</span> = <span class="built_in">require</span>(<span class="string">&#x27;source-map&#x27;</span>).<span class="property">SourceMapConsumer</span></span><br><span class="line"><span class="keyword">var</span> exec = <span class="built_in">require</span>(<span class="string">&#x27;child_process&#x27;</span>).<span class="property">exec</span></span><br><span class="line"><span class="keyword">var</span> lineno = process.<span class="property">argv</span>[<span class="number">2</span>] || <span class="number">0</span>            <span class="comment">// 行数</span></span><br><span class="line"><span class="keyword">var</span> columnno = process.<span class="property">argv</span>[<span class="number">3</span>] || <span class="number">0</span>          <span class="comment">// 列数</span></span><br><span class="line"><span class="keyword">var</span> fileName = process.<span class="property">argv</span>[<span class="number">4</span>] || <span class="string">&#x27;&#x27;</span>         <span class="comment">// 错误文件名</span></span><br><span class="line"><span class="title function_">exec</span>(<span class="string">&#x27;node build onlineMap&#x27;</span>, <span class="keyword">function</span> (<span class="params"></span>) &#123;   <span class="comment">// 运行构建线上代码的命令</span></span><br><span class="line">    <span class="comment">// 读取错误文件对应的 map 文件并输出错误信息：+ 是为了转化为数字</span></span><br><span class="line">    <span class="keyword">let</span> file = <span class="string">`./dist/<span class="subst">$&#123;fileName&#125;</span>.map`</span></span><br><span class="line">    <span class="keyword">var</span> consumer = <span class="keyword">new</span> <span class="title class_">SourceMapConsumer</span>(fs.<span class="title function_">readFileSync</span>(file, <span class="string">&#x27;utf8&#x27;</span>))</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(consumer.<span class="title function_">originalPositionFor</span>(&#123;</span><br><span class="line">        <span class="attr">line</span>: +lineno,</span><br><span class="line">        <span class="attr">column</span>: +columnno</span><br><span class="line">    &#125;))</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">errorLoggerMiddleware</span> = (<span class="params">err, req, res, next</span>) =&gt; &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">error</span>(<span class="string">&quot;ERROR: &quot;</span>, err)</span><br><span class="line">    <span class="title function_">next</span>(err)</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">const</span> <span class="title function_">returnErrorMiddleware</span> = (<span class="params">err, req, res, next</span>) =&gt; &#123;</span><br><span class="line">    res.<span class="title function_">status</span>(err.<span class="property">statusCode</span> || <span class="number">500</span>).<span class="title function_">send</span>(err.<span class="property">message</span>)</span><br><span class="line">&#125;</span><br><span class="line">app.<span class="title function_">use</span>(errorLoggerMiddleware)</span><br><span class="line">app.<span class="title function_">use</span>(returnErrorMiddleware)</span><br><span class="line"></span><br><span class="line">process.<span class="title function_">on</span>(<span class="string">&#x27;uncaughtException&#x27;</span>, <span class="function"><span class="params">error</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">error</span>(<span class="string">&quot;所有未捕获的异常: &quot;</span>, err)</span><br><span class="line">&#125;)</span><br><span class="line">process.<span class="title function_">on</span>(<span class="string">&#x27;unhandledRejection&#x27;</span>, <span class="function"><span class="params">error</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">error</span>(<span class="string">&quot;所有未处理的 Promise 拒绝: &quot;</span>, err)</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>


<h2 id="监控接口"><a href="#监控接口" class="headerlink" title="监控接口"></a>监控接口</h2>  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@title</span> 1、性能监控</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> 引用：PerformanceMonitor.getInstance();</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> 源码：https://github.com/paul-wj/blog/tree/master</span></span><br><span class="line"><span class="comment">**/</span> </span><br><span class="line">interface <span class="title class_">PageTimingInfo</span> &#123;</span><br><span class="line">    <span class="attr">fpt</span>: number;        <span class="comment">// 首次渲染/白屏时间：请求开始到浏览器开始解析 HTML。</span></span><br><span class="line">    <span class="attr">tti</span>: number;        <span class="comment">// 首次可交互时间：浏览器完成解析 HTML 和构建 DOM。</span></span><br><span class="line">    <span class="attr">ready</span>: number;      <span class="comment">// HTML 加载时间：同步JS执行时间 = ready - tti。</span></span><br><span class="line">    <span class="attr">loadTime</span>: number;   <span class="comment">// 页面完全加载时间：fpt + ready + 资源加载时间。</span></span><br><span class="line">    <span class="attr">firstbyte</span>: number;  <span class="comment">// 首包时间：客户端开始请求到服务端响应的第一个包。</span></span><br><span class="line">    <span class="attr">dns</span>: number;        <span class="comment">// DNS 查询耗时</span></span><br><span class="line">    <span class="attr">tcp</span>: number;        <span class="comment">// TCP 连接耗时</span></span><br><span class="line">    <span class="attr">ttfb</span>: number;       <span class="comment">// 请求响应耗时</span></span><br><span class="line">    <span class="attr">trans</span>: number;      <span class="comment">// 内容传输耗时</span></span><br><span class="line">    <span class="attr">dom</span>: number;        <span class="comment">// DOM 解析耗时</span></span><br><span class="line">    <span class="attr">res</span>: number;        <span class="comment">// 资源加载耗时：页面中的同步加载资源。</span></span><br><span class="line">    <span class="attr">sslTime</span>: number;    <span class="comment">// SSL 安全连接耗时：只在 HTTPS 有效。</span></span><br><span class="line">    <span class="attr">type</span>: string;       <span class="comment">// navigate 跳转、reload 重新加载。</span></span><br><span class="line">    <span class="attr">url</span>: string;        <span class="comment">// 当前地址</span></span><br><span class="line">    <span class="attr">userAgent</span>: string;  <span class="comment">// 浏览器的标识</span></span><br><span class="line">    <span class="attr">compressionRatio</span>: number;  <span class="comment">// 压缩率</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">class</span> <span class="title class_">PerformanceMonitor</span>&#123;</span><br><span class="line">    <span class="keyword">static</span> <span class="attr">instance</span>: <span class="title class_">PerformanceMonitor</span>;</span><br><span class="line">    <span class="keyword">static</span> <span class="attr">performance</span>: <span class="title class_">Performance</span>;</span><br><span class="line">    <span class="keyword">static</span> <span class="variable constant_">TIMEOUT</span> = <span class="number">5000</span>;</span><br><span class="line"></span><br><span class="line">    <span class="title function_">constructor</span>(<span class="params"></span>) &#123;</span><br><span class="line">        <span class="keyword">const</span> performance = <span class="variable language_">window</span>.<span class="property">performance</span> || <span class="variable language_">window</span>.<span class="property">msPerformance</span> </span><br><span class="line">        || <span class="variable language_">window</span>.<span class="property">webkitPerformance</span>;</span><br><span class="line">        <span class="keyword">if</span> (!performance) <span class="keyword">return</span>;</span><br><span class="line">        <span class="title class_">PerformanceMonitor</span>.<span class="property">performance</span> = performance;</span><br><span class="line">        <span class="title class_">PerformanceMonitor</span>.<span class="title function_">init</span>();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 单例模式：不是实例则实例化后返回</span></span><br><span class="line">    <span class="keyword">static</span> <span class="title function_">getInstance</span>(): <span class="title class_">PerformanceMonitor</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (!<span class="title class_">PerformanceMonitor</span>.<span class="property">instance</span>) &#123;</span><br><span class="line">            <span class="title class_">PerformanceMonitor</span>.<span class="property">instance</span> = <span class="keyword">new</span> <span class="title class_">PerformanceMonitor</span>();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="title class_">PerformanceMonitor</span>.<span class="property">instance</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="title function_">init</span>(): <span class="keyword">void</span> &#123;</span><br><span class="line">        <span class="title class_">PerformanceMonitor</span>.<span class="title function_">bindEvent</span>();</span><br><span class="line">    &#125;;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="title function_">bindEvent</span>(): <span class="keyword">void</span> &#123;</span><br><span class="line">        <span class="keyword">const</span> <span class="attr">preLoad</span>: <span class="function">(<span class="params">e: Event</span>) =&gt;</span> any = <span class="variable language_">window</span>.<span class="property">onload</span>;</span><br><span class="line">        <span class="variable language_">window</span>.<span class="property">onload</span> = <span class="function">(<span class="params">e: Event</span>) =&gt;</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (preLoad &amp;&amp; <span class="keyword">typeof</span> preLoad === <span class="string">&#x27;function&#x27;</span>) &#123;</span><br><span class="line">                <span class="title function_">preLoad</span>(e);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">// 尽量不影响页面主线程</span></span><br><span class="line">            <span class="keyword">let</span> logPackage = <span class="title class_">PerformanceMonitor</span>.<span class="property">logPackage</span></span><br><span class="line">            <span class="keyword">if</span> (<span class="variable language_">window</span>.<span class="property">requestIdleCallback</span>) &#123;</span><br><span class="line">                <span class="variable language_">window</span>.<span class="title function_">requestIdleCallback</span>(logPackage);</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="built_in">setTimeout</span>(logPackage);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 获取页面加载数据</span></span><br><span class="line">    <span class="keyword">static</span> <span class="title function_">getPageTimeData</span>(<span class="attr">isTiming</span>: boolean): <span class="title class_">PageTimingInfo</span> &#123;</span><br><span class="line">        <span class="keyword">const</span> &#123;</span><br><span class="line">            fetchStart,</span><br><span class="line">            domainLookupStart,</span><br><span class="line">            domainLookupEnd,</span><br><span class="line">            connectStart,</span><br><span class="line">            connectEnd,</span><br><span class="line">            secureConnectionStart,</span><br><span class="line">            requestStart,</span><br><span class="line">            responseStart,</span><br><span class="line">            responseEnd,</span><br><span class="line">            domInteractive,</span><br><span class="line">            domContentLoadedEventStart,</span><br><span class="line">            domContentLoadedEventEnd,</span><br><span class="line">            loadEventStart,</span><br><span class="line">        &#125; = isTiming ? <span class="title class_">PerformanceMonitor</span>.<span class="property">performance</span>.<span class="property">timing</span> : </span><br><span class="line">        <span class="title class_">PerformanceMonitor</span>.<span class="property">performance</span>.<span class="title function_">getEntriesByType</span>(<span class="string">&#x27;navigation&#x27;</span>);</span><br><span class="line">        <span class="keyword">return</span> &#123;</span><br><span class="line">            <span class="attr">fpt</span>: responseEnd - fetchStart,</span><br><span class="line">            <span class="attr">tti</span>: domInteractive - fetchStart,</span><br><span class="line">            <span class="attr">ready</span>: domContentLoadedEventStart - fetchStart,</span><br><span class="line">            <span class="attr">loadTime</span>: loadEventStart - fetchStart,</span><br><span class="line">            <span class="attr">firstbyte</span>: responseStart - domainLookupStart,</span><br><span class="line">            <span class="attr">dns</span>: domainLookupEnd - domainLookupStart,</span><br><span class="line">            <span class="attr">tcp</span>: connectEnd - connectStart,</span><br><span class="line">            <span class="attr">ttfb</span>: responseStart - requestStart,</span><br><span class="line">            <span class="attr">trans</span>: responseEnd - responseStart,</span><br><span class="line">            <span class="attr">dom</span>: domInteractive - responseEnd,</span><br><span class="line">            <span class="attr">res</span>: loadEventStart - domContentLoadedEventEnd,</span><br><span class="line">            <span class="attr">sslTime</span>: secureConnectionStart ? (connectEnd - </span><br><span class="line">            secureConnectionStart) : <span class="literal">null</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 获取页面资源加载数据</span></span><br><span class="line">    <span class="keyword">static</span> <span class="title function_">getTimeoutRes</span>(<span class="params"></span>) &#123;</span><br><span class="line">        <span class="keyword">const</span> resourceTimes = <span class="title class_">PerformanceMonitor</span>.<span class="property">performance</span></span><br><span class="line">        .<span class="title function_">getEntriesByType</span>(<span class="string">&#x27;resource&#x27;</span>) || [];</span><br><span class="line">        <span class="keyword">return</span> resourceTimes.<span class="title function_">filter</span>(<span class="function">(<span class="params">&#123;startTime, responseEnd&#125;</span>) =&gt;</span> (</span><br><span class="line">            responseEnd - startTime &gt; <span class="title class_">PerformanceMonitor</span>.<span class="property">TIMEOUT</span></span><br><span class="line">        )).<span class="title function_">map</span>(<span class="function"><span class="params">item</span> =&gt;</span> (&#123;</span><br><span class="line">            <span class="attr">name</span>: item.<span class="property">name</span>, </span><br><span class="line">            <span class="attr">encodedBodySize</span>: item.<span class="property">encodedBodySize</span>, </span><br><span class="line">            <span class="attr">decodedBodySize</span>: item.<span class="property">decodedBodySize</span>, </span><br><span class="line">            <span class="attr">timeout</span>: <span class="title class_">PerformanceMonitor</span>.<span class="property">TIMEOUT</span>, </span><br><span class="line">            <span class="attr">duration</span>: item.<span class="property">duration</span>, </span><br><span class="line">            <span class="attr">protocol</span>: item.<span class="property">nextHopProtocol</span>, </span><br><span class="line">            <span class="attr">type</span>: item.<span class="property">initiatorType</span></span><br><span class="line">        &#125;));</span><br><span class="line">    &#125;;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">static</span> <span class="title function_">logPackage</span>(): <span class="keyword">void</span> &#123;</span><br><span class="line">        <span class="keyword">const</span> &#123;performance&#125; = <span class="title class_">PerformanceMonitor</span>;</span><br><span class="line">        <span class="keyword">const</span> resourceList = <span class="title class_">PerformanceMonitor</span>.<span class="title function_">getTimeoutRes</span>();</span><br><span class="line">        <span class="keyword">const</span> &#123;userAgent&#125; = navigator;</span><br><span class="line">        <span class="keyword">const</span> &#123;<span class="attr">location</span>: &#123;<span class="attr">href</span>: url&#125;&#125; = <span class="variable language_">window</span>;</span><br><span class="line">        <span class="keyword">let</span> pageTimeData;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 判断 getEntriesByType 是否可用，不可用使用 timing（兼容性处理）</span></span><br><span class="line">        <span class="keyword">if</span> (performance.<span class="title function_">getEntriesByType</span>(<span class="string">&#x27;navigation&#x27;</span>).<span class="property">length</span> &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            pageTimeData = <span class="title class_">PerformanceMonitor</span>.<span class="title function_">getPageTimeData</span>(<span class="literal">false</span>);</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (performance.<span class="property">timing</span>) &#123;</span><br><span class="line">            pageTimeData = <span class="title class_">PerformanceMonitor</span>.<span class="title function_">getPageTimeData</span>(<span class="literal">true</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 上传页面数据、资源文件</span></span><br><span class="line">        <span class="keyword">if</span> (pageTimeData) &#123;</span><br><span class="line">            axios.<span class="title function_">post</span>(<span class="string">&#x27;/performance&#x27;</span>, &#123;...pageTimeData, userAgent&#125;);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (resourceList.<span class="property">length</span>) &#123;</span><br><span class="line">            axios.<span class="title function_">post</span>(<span class="string">&#x27;/resource&#x27;</span>, &#123;resourceList, url, userAgent&#125;);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">=====================================================</span><br><span class="line"><span class="comment">// 2、错误监控：引用通过 ErrorMonitor.getInstance();</span></span><br><span class="line"><span class="keyword">import</span> <span class="title class_">TraceKit</span> <span class="keyword">from</span> <span class="string">&#x27;tracekit&#x27;</span>;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">class</span> <span class="title class_">ErrorMonitor</span> &#123;</span><br><span class="line">    private <span class="keyword">static</span> <span class="attr">instance</span>: <span class="title class_">ErrorMonitor</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="title function_">getInstance</span>(): <span class="title class_">ErrorMonitor</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (!<span class="title class_">ErrorMonitor</span>.<span class="property">instance</span>) &#123;</span><br><span class="line">            <span class="title class_">ErrorMonitor</span>.<span class="property">instance</span> = <span class="keyword">new</span> <span class="title class_">ErrorMonitor</span>()</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="title class_">ErrorMonitor</span>.<span class="property">instance</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="title function_">constructor</span>(<span class="params"></span>) &#123;</span><br><span class="line">        <span class="title class_">ErrorMonitor</span>.<span class="title function_">init</span>();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 常见语法、同步、异步错误等错误回调</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> <span class="variable">message</span></span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> <span class="variable">url</span></span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> <span class="variable">lineNum</span></span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> <span class="variable">columnNum</span></span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> <span class="variable">error</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    protected <span class="keyword">static</span> <span class="title function_">windowErrorHandle</span>(<span class="attr">message</span>: string, <span class="attr">url</span>: string, </span><br><span class="line">    <span class="attr">lineNum</span>: number, <span class="attr">columnNum</span>: number, <span class="attr">error</span>: <span class="title class_">Error</span>): boolean &#123;</span><br><span class="line">        <span class="keyword">const</span> &#123;<span class="attr">location</span>: &#123;href&#125;&#125; = <span class="variable language_">window</span>;</span><br><span class="line">        axios.<span class="title function_">post</span>(<span class="string">&#x27;/monitor/web/error&#x27;</span>, &#123;</span><br><span class="line">            href,       <span class="comment">// 报错页面地址</span></span><br><span class="line">            message,    <span class="comment">// 错误描述</span></span><br><span class="line">            url,        <span class="comment">// 报错文件</span></span><br><span class="line">            lineNum,    <span class="comment">// 报错行号</span></span><br><span class="line">            columnNum,  <span class="comment">// 报错列号</span></span><br><span class="line">            <span class="attr">error</span>: <span class="title class_">JSON</span>.<span class="title function_">stringify</span>(error)</span><br><span class="line">        &#125;);</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 资源加载错误回调</span></span><br><span class="line">    protected <span class="keyword">static</span> <span class="title function_">networkErrorHandle</span>(<span class="params">e: Event</span>) &#123;</span><br><span class="line">        <span class="keyword">const</span> target = e.<span class="property">target</span> || e.<span class="property">srcElement</span>;</span><br><span class="line">        <span class="keyword">if</span> (target === <span class="variable language_">window</span>) <span class="keyword">return</span></span><br><span class="line">        <span class="keyword">const</span> &#123;<span class="attr">location</span>: &#123;href&#125;&#125; = <span class="variable language_">window</span>;</span><br><span class="line">        <span class="keyword">const</span> &#123;src&#125; = target <span class="keyword">as</span> any;</span><br><span class="line">        axios.<span class="title function_">post</span>(<span class="string">&#x27;/monitor/web/error-resource&#x27;</span>, &#123;href, src&#125;);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Promise 错误回调：TraceKit 实现跨浏览器堆栈跟踪日志标准化</span></span><br><span class="line">    protected <span class="keyword">static</span> <span class="title function_">promiseErrorHandle</span>(<span class="params">e: ErrorEvent</span>) &#123;</span><br><span class="line">        e.<span class="title function_">preventDefault</span>();</span><br><span class="line">        <span class="title class_">TraceKit</span>.<span class="title function_">report</span>((e <span class="keyword">as</span> any).<span class="property">reason</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    protected <span class="keyword">static</span> <span class="title function_">bindEvent</span>(<span class="params"></span>) &#123;</span><br><span class="line">        <span class="variable language_">window</span>.<span class="property">onerror</span> = <span class="title class_">ErrorMonitor</span>.<span class="property">windowErrorHandle</span>;</span><br><span class="line">        <span class="keyword">const</span> &#123;networkErrorHandle, promiseErrorHandle&#125; = <span class="title class_">ErrorMonitor</span>;</span><br><span class="line">        <span class="variable language_">window</span>.<span class="title function_">addEventListener</span>(<span class="string">&#x27;error&#x27;</span>, networkErrorHandle, <span class="literal">true</span>);</span><br><span class="line">        <span class="variable language_">window</span>.<span class="title function_">addEventListener</span>(<span class="string">&#x27;unhandledrejection&#x27;</span>, promiseErrorHandle);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    protected <span class="keyword">static</span> <span class="title function_">init</span>(<span class="params"></span>) &#123;</span><br><span class="line">        <span class="title class_">ErrorMonitor</span>.<span class="title function_">bindEvent</span>();</span><br><span class="line">    &#125;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>


<h2 id="集成-Node"><a href="#集成-Node" class="headerlink" title="集成 Node"></a>集成 Node</h2><blockquote>
<p>Node 性能监控主要如下：</p>
</blockquote>
<ul>
<li><strong>日志监控</strong>：展示异常类型和数量，监控 pv&#x2F;uv 了解用户习惯。</li>
<li><strong>响应时间</strong>：可通过 nginx 反向代理、通过应用的操作日志监控。</li>
<li><strong>进程监控</strong>：多进程应用的运行进程数如果低于低估值应发出警告。</li>
<li><strong>磁盘监控</strong>：为磁盘使用量设置上限，写日志等使用量超出时警告。</li>
<li><strong>内存监控</strong>：内存使用量应随着访问量上升回落，只升不降则内存泄漏，突然异常则追踪改动。</li>
<li><strong>cpu 占用监控</strong>：分为用户态(应用开销)、内核态(进程调度或系统调用)、IOWait (cpu等待磁盘I&#x2F;O)。正常范围应为用户态小于 70%、内核态小于 35%、整体小于 70%，可设置阈值。</li>
<li><strong>cpu load 监控</strong>：又称 cpu 平均负载，即单位时间内正在使用和等待使用 cpu 的平均任务数。过高则进程数量过多，node 的进程模块反复启动新进程，可监控该值以防意外。</li>
<li><strong>I&#x2F;O 负载</strong>：主要是反应磁盘读写情况的 磁盘 I&#x2F;O。对于主要面向网络业务的 node 应用一般不会出现负载过高的情况，大多数压力来自于数据库，但也应该监控该值防止意外情况。</li>
<li><strong>网络监控</strong>：监控流入流量和流出流量并设置上限值。流量暴涨时可通过数值感知到网站宣传是否有效，流量超过警戒值时应找出增长原因，正常增长则应该评估是否增加硬件设备。</li>
<li><strong>应用状态监控</strong>：除了硬性需要检测的指标之外，应用还应该提供一种机制来反馈其自身的状态信息，外部监控将会持续性地调用应用的反馈接口来检查健康状态。</li>
<li><strong>dns 域名监控</strong>：一般很稳定，但是一旦出现故障就会造成程序崩溃。</li>
</ul>
  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br></pre></td><td class="code"><pre><span class="line">===================== <span class="number">1</span>、异常收集 ====================</span><br><span class="line"><span class="comment">// 1.1、搭建 Egg.js 工程</span></span><br><span class="line">npm i egg-init -g</span><br><span class="line">egg-init backend --type=simple</span><br><span class="line">cd backend</span><br><span class="line">npm i</span><br><span class="line">npm run dev</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@title</span> 1.2、编写 error 上传接口</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> app/router.js 添加新路由</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> app/controller/monitor 创建新 controller</span></span><br><span class="line"><span class="comment">**/</span></span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = <span class="function"><span class="params">app</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> &#123; router, controller &#125; = app;</span><br><span class="line">    router.<span class="title function_">get</span>(<span class="string">&#x27;/&#x27;</span>, controller.<span class="property">home</span>.<span class="property">index</span>);</span><br><span class="line">    router.<span class="title function_">get</span>(<span class="string">&#x27;/monitor/error&#x27;</span>, controller.<span class="property">monitor</span>.<span class="property">index</span>);</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="title class_">Controller</span> = <span class="built_in">require</span>(<span class="string">&#x27;egg&#x27;</span>).<span class="property">Controller</span>;</span><br><span class="line"><span class="keyword">const</span> &#123; getOriginSource &#125; = <span class="built_in">require</span>(<span class="string">&#x27;../utils/sourcemap&#x27;</span>)</span><br><span class="line"><span class="keyword">const</span> fs = <span class="built_in">require</span>(<span class="string">&#x27;fs&#x27;</span>)</span><br><span class="line"><span class="keyword">const</span> path = <span class="built_in">require</span>(<span class="string">&#x27;path&#x27;</span>)</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MonitorController</span> <span class="keyword">extends</span> <span class="title class_ inherited__">Controller</span> &#123;</span><br><span class="line">    <span class="keyword">async</span> <span class="title function_">index</span>(<span class="params"></span>) &#123;</span><br><span class="line">        <span class="keyword">const</span> &#123; ctx &#125; = <span class="variable language_">this</span>;</span><br><span class="line">        <span class="keyword">const</span> &#123; info &#125; = ctx.<span class="property">query</span></span><br><span class="line">        <span class="keyword">const</span> baseInfo = <span class="title class_">Buffer</span>.<span class="title function_">from</span>(info, <span class="string">&#x27;base64&#x27;</span>)</span><br><span class="line">        <span class="keyword">const</span> json = <span class="title class_">JSON</span>.<span class="title function_">parse</span>(baseInfo.<span class="title function_">toString</span>(<span class="string">&#x27;utf-8&#x27;</span>))</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">error</span>(<span class="string">&#x27;fronterror:&#x27;</span>, json)</span><br><span class="line">        ctx.<span class="property">body</span> = <span class="string">&#x27;&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = <span class="title class_">MonitorController</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@title</span> 1.3、记入日志文件：fs 自定义或通过日志库 log4js</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> config/config.default.js 自定义一个日志配置</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> app/controller/monitor.js 添加日志记录</span></span><br><span class="line"><span class="comment">**/</span> </span><br><span class="line">config.<span class="property">customLogger</span> = &#123;</span><br><span class="line">    <span class="attr">frontendLogger</span>: &#123;</span><br><span class="line">        <span class="attr">file</span>: path.<span class="title function_">join</span>(appInfo.<span class="property">root</span>, <span class="string">&#x27;logs/frontend.log&#x27;</span>)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="title function_">index</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">const</span> &#123; ctx &#125; = <span class="variable language_">this</span>;</span><br><span class="line">    <span class="keyword">const</span> &#123; info &#125; = ctx.<span class="property">query</span></span><br><span class="line">    <span class="keyword">const</span> baseInfo = <span class="title class_">Buffer</span>.<span class="title function_">from</span>(info, <span class="string">&#x27;base64&#x27;</span>)</span><br><span class="line">    <span class="keyword">const</span> json = <span class="title class_">JSON</span>.<span class="title function_">parse</span>(baseInfo.<span class="title function_">toString</span>(<span class="string">&#x27;utf-8&#x27;</span>))</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">ctx</span>.<span class="title function_">getLogger</span>(<span class="string">&#x27;frontendLogger&#x27;</span>).<span class="title function_">error</span>(json)</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">error</span>(<span class="string">&#x27;fronterror:&#x27;</span>, json)</span><br><span class="line">    ctx.<span class="property">body</span> = <span class="string">&#x27;&#x27;</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">========== <span class="number">2</span>、异常分析：还原 webpack 混淆压缩的代码 ==========</span><br><span class="line"><span class="comment">// 2.1、source-map/plugin：Webpack 打包时自动上传 SourceMap</span></span><br><span class="line"><span class="keyword">const</span> fs = <span class="built_in">require</span>(<span class="string">&#x27;fs&#x27;</span>)</span><br><span class="line"><span class="keyword">const</span> http = <span class="built_in">require</span>(<span class="string">&#x27;http&#x27;</span>);</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">UploadSourceMapPlugin</span> &#123;</span><br><span class="line">    <span class="title function_">constructor</span>(<span class="params">options</span>) &#123;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">options</span> = options</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="title function_">apply</span>(<span class="params">compiler</span>) &#123;            <span class="comment">// 打包结束后执行</span></span><br><span class="line">        compiler.<span class="property">hooks</span>.<span class="property">done</span>.<span class="title function_">tap</span>(<span class="string">&quot;upload-sourcemap-plugin&quot;</span>, <span class="function"><span class="params">status</span> =&gt;</span> &#123;</span><br><span class="line">            <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;webpack runing&#x27;</span>)</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = <span class="title class_">UploadSourceMapPlugin</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 2.2、webpack.config.js：添加自动上传 Map 插件 </span></span><br><span class="line"><span class="title class_">UploadSourceMapPlugin</span> = <span class="built_in">require</span>(<span class="string">&#x27;./plugin/UploadSourceMapPlugin&#x27;</span>)</span><br><span class="line"><span class="attr">plugins</span>: [</span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">UploadSourceMapPlugin</span>(&#123;</span><br><span class="line">        <span class="attr">uploadUrl</span>:<span class="string">&#x27;http://localhost:7001/monitor/sourcemap&#x27;</span>,</span><br><span class="line">        <span class="attr">apiKey</span>: <span class="string">&#x27;kaikeba&#x27;</span></span><br><span class="line">    &#125;)</span><br><span class="line">]</span><br><span class="line"></span><br><span class="line"><span class="comment">// 2.3、plugin/uploadSourceMapWebPlugin.js：添加 map 读取逻辑</span></span><br><span class="line"><span class="keyword">const</span> glob = <span class="built_in">require</span>(<span class="string">&#x27;glob&#x27;</span>)</span><br><span class="line"><span class="keyword">const</span> path = <span class="built_in">require</span>(<span class="string">&#x27;path&#x27;</span>)</span><br><span class="line"><span class="title function_">apply</span>(<span class="params">compiler</span>) &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;打包结束后读取 sourcemap 文件&#x27;</span>)</span><br><span class="line">    compiler.<span class="property">hooks</span>.<span class="property">done</span>.<span class="title function_">tap</span>(<span class="string">&#x27;upload-sourcemap-plugin&#x27;</span>, <span class="keyword">async</span> status =&gt; &#123;</span><br><span class="line">        <span class="keyword">const</span> list = glob.<span class="title function_">sync</span>(path.<span class="title function_">join</span>(status.<span class="property">compilation</span>.<span class="property">outputOptions</span></span><br><span class="line">        .<span class="property">path</span>, <span class="string">`./**/*.&#123;js.map,&#125;`</span>))</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">let</span> filename <span class="keyword">of</span> list) &#123;</span><br><span class="line">            <span class="keyword">await</span> <span class="variable language_">this</span>.<span class="title function_">upload</span>(<span class="variable language_">this</span>.<span class="property">options</span>.<span class="property">uploadUrl</span>, filename)</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 2.4：实现 http 上传功能</span></span><br><span class="line"><span class="title function_">upload</span>(<span class="params">url, file</span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="function"><span class="params">resolve</span> =&gt;</span> &#123;</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;uploadMap:&#x27;</span>, file)</span><br><span class="line">        <span class="keyword">const</span> req = http.<span class="title function_">request</span>(<span class="string">`<span class="subst">$&#123;url&#125;</span>?name=<span class="subst">$&#123;path.basename(file)&#125;</span>`</span>, &#123;</span><br><span class="line">            <span class="attr">method</span>: <span class="string">&#x27;POST&#x27;</span>,</span><br><span class="line">            <span class="attr">headers</span>: &#123;</span><br><span class="line">              <span class="string">&#x27;Content-Type&#x27;</span>: <span class="string">&#x27;application/octet-stream&#x27;</span>,</span><br><span class="line">              <span class="title class_">Connection</span>: <span class="string">&quot;keep-alive&quot;</span>,</span><br><span class="line">              <span class="string">&quot;Transfer-Encoding&quot;</span>: <span class="string">&quot;chunked&quot;</span></span><br><span class="line">            &#125;</span><br><span class="line">        &#125;)</span><br><span class="line">        fs.<span class="title function_">createReadStream</span>(file).<span class="title function_">on</span>(<span class="string">&quot;data&quot;</span>, <span class="function"><span class="params">chunk</span> =&gt;</span> &#123;</span><br><span class="line">            req.<span class="title function_">write</span>(chunk);  <span class="comment">// 发送数据</span></span><br><span class="line">        &#125;)</span><br><span class="line">        .<span class="title function_">on</span>(<span class="string">&quot;end&quot;</span>, <span class="function">() =&gt;</span> &#123;</span><br><span class="line">            req.<span class="title function_">end</span>();         <span class="comment">// 发送结束</span></span><br><span class="line">            <span class="title function_">resolve</span>()</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@title</span> 2.5、服务器端添加上传接口</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> backend/app/router.js</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> backend/app/controller/monitor.js</span></span><br><span class="line"><span class="comment">**/</span> </span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = <span class="function"><span class="params">app</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> &#123; router, controller &#125; = app;</span><br><span class="line">    router.<span class="title function_">get</span>(<span class="string">&#x27;/&#x27;</span>, controller.<span class="property">home</span>.<span class="property">index</span>);</span><br><span class="line">    router.<span class="title function_">get</span>(<span class="string">&#x27;/monitor/error&#x27;</span>, controller.<span class="property">monitor</span>.<span class="property">index</span>);</span><br><span class="line">    router.<span class="title function_">post</span>(<span class="string">&#x27;/monitor/sourcemap&#x27;</span>,controller.<span class="property">monitor</span>.<span class="property">upload</span>)</span><br><span class="line">&#125;</span><br><span class="line">  </span><br><span class="line"><span class="keyword">async</span> <span class="title function_">upload</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">const</span> &#123; ctx &#125; = <span class="variable language_">this</span></span><br><span class="line">    <span class="keyword">const</span> stream = ctx.<span class="property">req</span></span><br><span class="line">    <span class="keyword">const</span> filename = ctx.<span class="property">query</span>.<span class="property">name</span></span><br><span class="line">    <span class="keyword">const</span> dir = path.<span class="title function_">join</span>(<span class="variable language_">this</span>.<span class="property">config</span>.<span class="property">baseDir</span>, <span class="string">&#x27;uploads&#x27;</span>)</span><br><span class="line">    <span class="keyword">if</span> (!fs.<span class="title function_">existsSync</span>(dir)) &#123; fs.<span class="title function_">mkdirSync</span>(dir) &#125;</span><br><span class="line">    <span class="keyword">const</span> target = path.<span class="title function_">join</span>(dir, filename)</span><br><span class="line">    <span class="keyword">const</span> writeStream = fs.<span class="title function_">createWriteStream</span>(target)</span><br><span class="line">    stream.<span class="title function_">pipe</span>(writeStream)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">========= <span class="number">3</span>、解析 <span class="title class_">ErrorStack</span>：<span class="title class_">SourceMap</span> 还原为源码 ===========</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@title</span> 3.1、搭建 Jest 框架</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> utils/stackparser.js：将错误栈中的代码位置解析为源码位置</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> utils/stackparser.spec.js     测试文件</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> npx jest stackparser --watch  运行 Jest</span></span><br><span class="line"><span class="comment">**/</span> </span><br><span class="line"><span class="keyword">const</span> &#123; <span class="title class_">SourceMapConsumer</span> &#125; = <span class="built_in">require</span>(<span class="string">&quot;source-map&quot;</span>);</span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = <span class="keyword">class</span> <span class="title class_">StackPaser</span> &#123;</span><br><span class="line">    <span class="title function_">constructor</span>(<span class="params">sourceMapDir</span>) &#123;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">consumers</span> = &#123;&#125;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">sourceMapDir</span> = sourceMapDir</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">async</span> <span class="title function_">getOriginStack</span>(<span class="params">stackFrame</span>) &#123;</span><br><span class="line">        <span class="keyword">const</span> origin = []</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">let</span> v <span class="keyword">of</span> stackFrame) &#123;</span><br><span class="line">            origin.<span class="title function_">push</span>(<span class="keyword">await</span> <span class="variable language_">this</span>.<span class="title function_">getOriginPosition</span>(v))</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="title class_">Object</span>.<span class="title function_">keys</span>(<span class="variable language_">this</span>.<span class="property">consumers</span>).<span class="title function_">forEach</span>(<span class="function"><span class="params">key</span> =&gt;</span> &#123;</span><br><span class="line">            <span class="variable language_">this</span>.<span class="property">consumers</span>[key].<span class="title function_">destroy</span>()</span><br><span class="line">        &#125;)</span><br><span class="line">        <span class="keyword">return</span> origin</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">async</span> <span class="title function_">getOriginPosition</span>(<span class="params">stackFrame</span>) &#123;</span><br><span class="line">        <span class="keyword">let</span> &#123; columnNumber, lineNumber, fileName &#125; = stackFrame</span><br><span class="line">        fileName = path.<span class="title function_">basename</span>(fileName)</span><br><span class="line">        <span class="keyword">let</span> consumer = <span class="variable language_">this</span>.<span class="property">consumers</span>[fileName]</span><br><span class="line">        <span class="keyword">if</span> (consumer === <span class="literal">undefined</span>) &#123;  <span class="comment">// 读取 sourcemap</span></span><br><span class="line">            <span class="keyword">const</span> fileMap = fileName + <span class="string">&#x27;.map&#x27;</span></span><br><span class="line">            <span class="keyword">const</span> mapPath = path.<span class="title function_">resolve</span>(<span class="variable language_">this</span>.<span class="property">sourceMapDir</span>, fileMap)</span><br><span class="line">            <span class="keyword">if</span> (!fs.<span class="title function_">existsSync</span>(mapPath)) &#123; <span class="keyword">return</span> stackFrame &#125;</span><br><span class="line">            <span class="keyword">const</span> content = fs.<span class="title function_">readFileSync</span>(mapPath, <span class="string">&#x27;utf8&#x27;</span>)</span><br><span class="line">            consumer = <span class="keyword">await</span> <span class="keyword">new</span> <span class="title class_">SourceMapConsumer</span>(content, <span class="literal">null</span>);</span><br><span class="line">            <span class="variable language_">this</span>.<span class="property">consumers</span>[fileName] = consumer</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">const</span> parseData = consumer.<span class="title function_">originalPositionFor</span>(&#123; </span><br><span class="line">            <span class="attr">line</span>:lineNumber, </span><br><span class="line">            <span class="attr">column</span>:columnNumber </span><br><span class="line">        &#125;)</span><br><span class="line">        <span class="keyword">return</span> parseData</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="title class_">StackParser</span> = <span class="built_in">require</span>(<span class="string">&#x27;../stackparser&#x27;</span>)</span><br><span class="line"><span class="keyword">const</span> &#123; resolve &#125; = <span class="built_in">require</span>(<span class="string">&#x27;path&#x27;</span>)</span><br><span class="line"><span class="keyword">const</span> error = &#123;</span><br><span class="line">    <span class="attr">stack</span>: <span class="string">&#x27;ReferenceError: xxx is not defined\n&#x27;</span> +</span><br><span class="line">        <span class="string">&#x27;   at http://localhost:7001/public/bundle.e733b.js:1:32&#x27;</span>,</span><br><span class="line">    <span class="attr">message</span>: <span class="string">&#x27;Uncaught ReferenceError: xxx is not defined&#x27;</span>,</span><br><span class="line">    <span class="attr">filename</span>: <span class="string">&#x27;http://localhost:7001/public/bundle.e7874f5c33b.js&#x27;</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="title function_">it</span>(<span class="string">&#x27;stackparser on-the-fly&#x27;</span>, <span class="keyword">async</span> () =&gt; &#123;</span><br><span class="line">    <span class="keyword">const</span> &#123;stack, message&#125; = error</span><br><span class="line">    <span class="keyword">const</span> stackParser = <span class="keyword">new</span> <span class="title class_">StackParser</span>(__dirname)</span><br><span class="line">    <span class="keyword">const</span> stackFrame = stackParser.<span class="title function_">parseStackTrack</span>(stack, message)</span><br><span class="line">    <span class="keyword">const</span> originStack = <span class="keyword">await</span> stackParser.<span class="title function_">getOriginStack</span>(stackFrame)</span><br><span class="line">    <span class="title function_">expect</span>(originStack[<span class="number">0</span>]).<span class="title function_">toMatchObject</span>(&#123;   <span class="comment">// 断言 </span></span><br><span class="line">        <span class="attr">source</span>: <span class="string">&#x27;webpack:///src/index.js&#x27;</span>,</span><br><span class="line">        <span class="attr">line</span>: <span class="number">24</span>,</span><br><span class="line">        <span class="attr">column</span>: <span class="number">4</span>,</span><br><span class="line">        <span class="attr">name</span>: <span class="string">&#x27;xxx&#x27;</span></span><br><span class="line">    &#125;)</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 3.2、错误堆栈反序列 Error 对象</span></span><br><span class="line"><span class="keyword">const</span> <span class="title class_">ErrorStackParser</span> = <span class="built_in">require</span>(<span class="string">&#x27;error-stack-parser&#x27;</span>)</span><br><span class="line"><span class="title function_">parseStackTrack</span>(<span class="params">stack, message</span>) &#123;</span><br><span class="line">    <span class="keyword">const</span> error = <span class="keyword">new</span> <span class="title class_">Error</span>(message)</span><br><span class="line">    error.<span class="property">stack</span> = stack</span><br><span class="line">    <span class="keyword">const</span> stackFrame = <span class="title class_">ErrorStackParser</span>.<span class="title function_">parse</span>(error)</span><br><span class="line">    <span class="keyword">return</span> stackFrame</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 3.3、将源码位置记入日志</span></span><br><span class="line"><span class="keyword">async</span> <span class="title function_">index</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">const</span> baseInfo = <span class="title class_">Buffer</span>.<span class="title function_">from</span>(<span class="variable language_">this</span>.<span class="property">ctx</span>.<span class="property">query</span>.<span class="property">info</span>, <span class="string">&#x27;base64&#x27;</span>)</span><br><span class="line">    <span class="keyword">const</span> json = <span class="title class_">JSON</span>.<span class="title function_">parse</span>(baseInfo.<span class="title function_">toString</span>(<span class="string">&#x27;utf-8&#x27;</span>))</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">error</span>(<span class="string">&#x27;fronterror:&#x27;</span>, json)</span><br><span class="line">    <span class="comment">// 转换为源码位置</span></span><br><span class="line">    <span class="keyword">const</span> &#123; stack, message &#125; = json</span><br><span class="line">    <span class="keyword">const</span> baseDir = <span class="variable language_">this</span>.<span class="property">config</span>.<span class="property">baseDir</span></span><br><span class="line">    <span class="keyword">const</span> stackParser = <span class="keyword">new</span> <span class="title class_">StackParser</span>(path.<span class="title function_">join</span>(baseDir, <span class="string">&#x27;uploads&#x27;</span>))</span><br><span class="line">    <span class="keyword">const</span> stackFrame = stackParser.<span class="title function_">parseStackTrack</span>(stack, message)</span><br><span class="line">    <span class="keyword">const</span> originStack = <span class="keyword">await</span> stackParser.<span class="title function_">getOriginStack</span>(stackFrame)</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">ctx</span>.<span class="title function_">getLogger</span>(<span class="string">&#x27;frontendLogger&#x27;</span>).<span class="title function_">error</span>(json, originStack)</span><br><span class="line">    ctx.<span class="property">body</span> = <span class="string">&#x27;&#x27;</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





    </div>

    
    
    <div>
      
        <div>
    
        <div style="text-align:center;color: #ccc;font-size:14px;margin-top:20px;">-------------本文结束<i class="fa fa-paw"></i>感谢您的阅读-------------</div>
    
</div>

      
    </div>
    
        

<div>
<ul class="post-copyright">
  <li class="post-copyright-author">
    <strong>本文作者： </strong>chuangges
  </li>
  <li class="post-copyright-link">
    <strong>本文链接：</strong>
    <a href="https://chuangges.gitee.io/2022/10/21/17-%E5%89%8D%E7%AB%AF%E6%8A%80%E8%83%BD/" title="前端应该知道的技能栈">https://chuangges.gitee.io/2022/10/21/17-前端技能/</a>
  </li>
  <li class="post-copyright-license">
    <strong>版权声明： </strong>本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/zh_CN" rel="external nofollow noopener noreferrer" target="_blank"><i class="fab fa-fw fa-creative-commons"></i>BY-NC-SA</a> 许可协议。转载请注明出处！
  </li>
</ul>
</div>


      <footer class="post-footer">

        


        
    <div class="post-nav">
      <div class="post-nav-item">
    <a href="/2022/08/26/16-Nginx/" rel="prev" title="Nginx 基础使用">
      <i class="fa fa-chevron-left"></i> Nginx 基础使用
    </a></div>
      <div class="post-nav-item">
    <a href="/2022/11/10/18-%E5%BE%AE%E6%9C%8D%E5%8A%A1/" rel="next" title="微服务技术栈">
      微服务技术栈 <i class="fa fa-chevron-right"></i>
    </a></div>
    </div>
      </footer>
    
  </article>
  
  
  



          </div>
          

<script>
  window.addEventListener('tabs:register', () => {
    let { activeClass } = CONFIG.comments;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      let activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      let commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }
</script>

        </div>
          
  
  <div class="toggle sidebar-toggle">
    <span class="toggle-line toggle-line-first"></span>
    <span class="toggle-line toggle-line-middle"></span>
    <span class="toggle-line toggle-line-last"></span>
  </div>

  <aside class="sidebar">
    <div class="sidebar-inner">

      <ul class="sidebar-nav motion-element">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <!--noindex-->
      <div class="post-toc-wrap sidebar-panel">
          <div class="post-toc motion-element"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#%E4%B8%80%E3%80%81CentOS-%E7%B3%BB%E7%BB%9F"><span class="nav-number">1.</span> <span class="nav-text">一、CentOS 系统</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#Linux-%E5%91%BD%E4%BB%A4"><span class="nav-number">1.1.</span> <span class="nav-text">Linux 命令</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Nginx-%E6%9C%8D%E5%8A%A1"><span class="nav-number">1.2.</span> <span class="nav-text">Nginx 服务</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#NodeJS-%E7%8E%AF%E5%A2%83"><span class="nav-number">1.3.</span> <span class="nav-text">NodeJS 环境</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Docker-%E9%83%A8%E7%BD%B2"><span class="nav-number">1.4.</span> <span class="nav-text">Docker 部署</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Jenkins-%E5%B7%A5%E5%85%B7"><span class="nav-number">1.5.</span> <span class="nav-text">Jenkins 工具</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E4%BA%8C%E3%80%81Dumi-%E7%BB%84%E4%BB%B6%E6%96%87%E6%A1%A3"><span class="nav-number">2.</span> <span class="nav-text">二、Dumi 组件文档</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E6%9E%84%E5%BB%BA%E6%96%87%E6%A1%A3%E9%A1%B9%E7%9B%AE"><span class="nav-number">2.1.</span> <span class="nav-text">构建文档项目</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#lerna-%E7%AE%A1%E7%90%86%E5%8C%85"><span class="nav-number">2.2.</span> <span class="nav-text">lerna 管理包</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Prettier-%E4%BB%A3%E7%A0%81%E9%A3%8E%E6%A0%BC"><span class="nav-number">2.3.</span> <span class="nav-text">Prettier 代码风格</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#GitHooks-%E6%8F%90%E4%BA%A4%E8%A7%84%E8%8C%83"><span class="nav-number">2.4.</span> <span class="nav-text">GitHooks 提交规范</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E4%B8%89%E3%80%81GUI-%E6%A1%8C%E9%9D%A2%E5%BA%94%E7%94%A8"><span class="nav-number">3.</span> <span class="nav-text">三、GUI 桌面应用</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E8%B7%A8%E5%B9%B3%E5%8F%B0%E6%A1%86%E6%9E%B6"><span class="nav-number">3.1.</span> <span class="nav-text">跨平台框架</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%BA%94%E7%94%A8%E5%BC%80%E5%8F%91"><span class="nav-number">3.2.</span> <span class="nav-text">应用开发</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E5%9B%9B%E3%80%81Web-%E7%9B%91%E6%8E%A7%E5%B9%B3%E5%8F%B0"><span class="nav-number">4.</span> <span class="nav-text">四、Web 监控平台</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E6%95%B0%E6%8D%AE%E9%87%87%E9%9B%86"><span class="nav-number">4.1.</span> <span class="nav-text">数据采集</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%BC%82%E5%B8%B8%E4%B8%8A%E6%8A%A5"><span class="nav-number">4.2.</span> <span class="nav-text">异常上报</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#JS-%E5%BC%82%E5%B8%B8"><span class="nav-number">4.3.</span> <span class="nav-text">JS 异常</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E7%9B%91%E6%8E%A7%E6%8E%A5%E5%8F%A3"><span class="nav-number">4.4.</span> <span class="nav-text">监控接口</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E9%9B%86%E6%88%90-Node"><span class="nav-number">4.5.</span> <span class="nav-text">集成 Node</span></a></li></ol></li></ol></div>
      </div>
      <!--/noindex-->

      <div class="site-overview-wrap sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <img class="site-author-image" itemprop="image" alt="chuangges" src="" data-original="/images/chuang.png">
  <p class="site-author-name" itemprop="name">chuangges</p>
  <div class="site-description" itemprop="description">chuangge's blog.</div>
</div>
<div class="site-state-wrap motion-element">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
          <a href="/archives/">
        
          <span class="site-state-item-count">39</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
            <a href="/categories/">
          
        <span class="site-state-item-count">7</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
            <a href="/tags/">
          
        <span class="site-state-item-count">7</span>
        <span class="site-state-item-name">标签</span></a>
      </div>
  </nav>
</div>
  <div class="links-of-author motion-element">
      <span class="links-of-author-item">
        <a href="https://github.com/chuangges" title="GitHub → https:&#x2F;&#x2F;github.com&#x2F;chuangges" rel="external nofollow noopener noreferrer" target="_blank"><i class="fab fa-github fa-fw"></i>GitHub</a>
      </span>
      <span class="links-of-author-item">
        <a href="https://stackoverflow.com/yourname" title="StackOverflow → https:&#x2F;&#x2F;stackoverflow.com&#x2F;yourname" rel="external nofollow noopener noreferrer" target="_blank"><i class="fab fa-stack-overflow fa-fw"></i>StackOverflow</a>
      </span>
  </div>


  <div class="links-of-blogroll motion-element">
    <div class="links-of-blogroll-title"><i class="fa fa-link fa-fw"></i>
      友情链接
    </div>
    <ul class="links-of-blogroll-list">
        <li class="links-of-blogroll-item">
          <a href="https://juejin.im/" title="https:&#x2F;&#x2F;juejin.im&#x2F;" rel="external nofollow noopener noreferrer" target="_blank">掘金</a>
        </li>
        <li class="links-of-blogroll-item">
          <a href="https://www.nowcoder.com/" title="https:&#x2F;&#x2F;www.nowcoder.com&#x2F;" rel="external nofollow noopener noreferrer" target="_blank">牛客网</a>
        </li>
        <li class="links-of-blogroll-item">
          <a href="https://www.docschina.org/" title="https:&#x2F;&#x2F;www.docschina.org&#x2F;" rel="external nofollow noopener noreferrer" target="_blank">印记中文</a>
        </li>
        <li class="links-of-blogroll-item">
          <a href="http://tool.lu/" title="http:&#x2F;&#x2F;tool.lu&#x2F;" rel="external nofollow noopener noreferrer" target="_blank">在线工具</a>
        </li>
        <li class="links-of-blogroll-item">
          <a href="https://tinypng.com/" title="https:&#x2F;&#x2F;tinypng.com&#x2F;" rel="external nofollow noopener noreferrer" target="_blank">图片压缩</a>
        </li>
        <li class="links-of-blogroll-item">
          <a href="https://bigjpg.com/" title="https:&#x2F;&#x2F;bigjpg.com&#x2F;" rel="external nofollow noopener noreferrer" target="_blank">图片放大</a>
        </li>
        <li class="links-of-blogroll-item">
          <a href="https://tool.chinaz.com/" title="https:&#x2F;&#x2F;tool.chinaz.com&#x2F;" rel="external nofollow noopener noreferrer" target="_blank">站长工具</a>
        </li>
        <li class="links-of-blogroll-item">
          <a href="https://learn-anything.xyz/" title="https:&#x2F;&#x2F;learn-anything.xyz&#x2F;" rel="external nofollow noopener noreferrer" target="_blank">Learn-Anything</a>
        </li>
        <li class="links-of-blogroll-item">
          <a href="https://stackoverflow.com/" title="https:&#x2F;&#x2F;stackoverflow.com&#x2F;" rel="external nofollow noopener noreferrer" target="_blank">Stack-Overflow</a>
        </li>
    </ul>
  </div>

      </div>

    </div>
  </aside>
  <div id="sidebar-dimmer"></div>


      </div>
    </main>

    <footer class="footer">
      <div class="footer-inner">
        

        

<div class="copyright">
  
  &copy; 
  <span itemprop="copyrightYear">2024</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">chuangge</span>
    <span class="post-meta-divider">|</span>
    <span class="post-meta-item-icon">
      <i class="fa fa-chart-area"></i>
    </span>
    <span title="站点总字数">1m</span>
    <span class="post-meta-divider">|</span>
    <span class="post-meta-item-icon">
      <i class="fa fa-coffee"></i>
    </span>
    <span title="站点阅读时长">15:14</span>
</div>
  <div class="powered-by">由 <a href="https://hexo.io/" class="theme-link" rel="external nofollow noopener noreferrer" target="_blank">Hexo</a> & <a href="https://pisces.theme-next.org/" class="theme-link" rel="external nofollow noopener noreferrer" target="_blank">NexT.Pisces</a> 强力驱动
  </div>

        








      </div>
    </footer>
  </div>

  
  <script src="/lib/anime.min.js"></script>
  <script src="/lib/velocity/velocity.min.js"></script>
  <script src="/lib/velocity/velocity.ui.min.js"></script>

<script src="/js/utils.js"></script>

<script src="/js/motion.js"></script>


<script src="/js/schemes/pisces.js"></script>


<script src="/js/next-boot.js"></script>




  















  

  

<script src="/live2dw/lib/L2Dwidget.min.js?094cbace49a39548bed64abff5988b05"></script><script>L2Dwidget.init({"pluginRootPath":"live2dw/","pluginJsPath":"lib/","pluginModelPath":"assets/","model":{"jsonPath":"/live2dw/assets/tororo.model.json"},"display":{"position":"left","width":180,"height":360},"mobile":{"show":false},"react":{"opacityDefault":1,"opacityOnHover":1},"log":false,"tagMode":false});</script>
        <style>
            [bg-lazy] {
                background-image: none !important;
                background-color: #eee !important;
            }
        </style>
        <script>
            window.imageLazyLoadSetting = {
                isSPA: false,
                preloadRatio: 1,
                processImages: null,
            };
        </script><script>window.addEventListener("load",function(){var t=/\.(gif|jpg|jpeg|tiff|png)$/i,r=/^data:image\/[a-z]+;base64,/;Array.prototype.slice.call(document.querySelectorAll("img[data-original]")).forEach(function(a){var e=a.parentNode;"A"===e.tagName&&(e.href.match(t)||e.href.match(r))&&(e.href=a.dataset.original)})});</script><script>!function(e){e.imageLazyLoadSetting.processImages=t;var n=e.imageLazyLoadSetting.isSPA,i=e.imageLazyLoadSetting.preloadRatio||1,r=o();function o(){var t=Array.prototype.slice.call(document.querySelectorAll("img[data-original]")),e=Array.prototype.slice.call(document.querySelectorAll("[bg-lazy]"));return t.concat(e)}function t(){n&&(r=o());for(var t,a=0;a<r.length;a++)0<=(t=(t=r[a]).getBoundingClientRect()).bottom&&0<=t.left&&t.top<=(e.innerHeight*i||document.documentElement.clientHeight*i)&&function(){var t,e,n,i,o=r[a];e=function(){r=r.filter(function(t){return o!==t})},(t=o).hasAttribute("bg-lazy")?(t.removeAttribute("bg-lazy"),e&&e()):(n=new Image,i=t.getAttribute("data-original"),n.onload=function(){t.src=i,t.removeAttribute("data-original"),e&&e()},t.src!==i&&(n.src=i))}()}function a(){clearTimeout(t.tId),t.tId=setTimeout(t,500)}t(),document.addEventListener("scroll",a),e.addEventListener("resize",a),e.addEventListener("orientationchange",a)}(this);</script></body>
</html>
